diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..1bccc1fa8 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.h5 filter=lfs diff=lfs merge=lfs -text diff --git a/README.md b/README.md index 0671349a6..33b881b4a 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ The SDK consists of: * [Spot API protocol definition](protos/bosdyn/api/README.md). This reference guide covers the details of the protocol applications used to communicate to Spot. Application developers who wish to use a language other than Python can implement clients that speak the protocol. * [Spot SDK Repository](https://github.com/boston-dynamics/spot-sdk). The GitHub repo where all of the Spot SDK code is hosted. -This is version 2.3.5 of the SDK. Please review the [Release Notes](docs/release_notes.md) to see what has changed. +This is version 3.0.0 of the SDK. Please review the [Release Notes](docs/release_notes.md) to see what has changed. ## Contents diff --git a/VERSION b/VERSION index cc6c9a491..4a36342fc 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.3.5 +3.0.0 diff --git a/choreography_protos/bosdyn/api/choreography_reference.md b/choreography_protos/bosdyn/api/choreography_reference.md new file mode 100644 index 000000000..3547d1c0f --- /dev/null +++ b/choreography_protos/bosdyn/api/choreography_reference.md @@ -0,0 +1,1891 @@ + + + + + + +# spot/choreography_params.proto + + + + + +### AnimateParams + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| animation_name | [string](#string) | The name of the animated move. There are no default values/bounds associated with this field. | +| body_entry_slices | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How many slices to smoothly transition from previous pose to animation. | +| body_exit_slices | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How many slices to return from animation to nominal pose. Zero indicates to keep final animated pose. | +| translation_multiplier | [bosdyn.api.Vec3Value](#bosdyn.api.Vec3Value) | Multiplier for animated translation by axis to exaggerate or suppress motion along specific axes. | +| rotation_multiplier | [EulerZYXValue](#bosdyn.api.spot.EulerZYXValue) | Multiplier for the animated orientation by axis to exaggerate or suppress motion along specific axes. | +| arm_entry_slices | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How many slices to smoothly transition from previous pose to animation. | +| shoulder_0_offset | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Joint angle offsets in radians for the arm joints. | +| shoulder_1_offset | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| elbow_0_offset | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| elbow_1_offset | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| wrist_0_offset | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| wrist_1_offset | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| gripper_offset | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| speed | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How fast to playback. 1.0 is normal speed. larger is faster. | +| offset_slices | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How late into the nominal script to start. | +| gripper_multiplier | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Multiply all gripper angles by this value. | +| gripper_strength_fraction | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How hard the gripper can squeeze. Fraction of full strength. | +| arm_dance_frame_id | [google.protobuf.Int32Value](#google.protobuf.Int32Value) | Which dance frame to use as a reference for workspace arm moves. Including this parameter overrides the animation frame. | +| body_tracking_stiffness | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How hard to try to track the animated body motion. Only applicable to animations that control both the body and the legs. On a scale of 1 to 10 (11 for a bit extra). Higher will result in more closely tracking the animated body motion, but possibly at the expense of balance for more difficult animations. | + + + + + + + + +### ArmMoveParams + +Parameters specific to ArmMove move. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| shoulder_0 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Joint angles in radians for the arm joints. | +| shoulder_1 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| elbow_0 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| elbow_1 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| wrist_0 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| wrist_1 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| easing | [Easing](#bosdyn.api.spot.Easing) | How the motion should be paced. | +| gripper | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Movement for the gripper. | + + + + + + + + +### BodyHoldParams + +Parameters specific to the BodyHold move. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| rotation | [EulerZYXValue](#bosdyn.api.spot.EulerZYXValue) | The robot will rotate its body to the specified orientation (roll/pitch/yaw) [rad]. | +| translation | [bosdyn.api.Vec3Value](#bosdyn.api.Vec3Value) | The positional offset to the robot's current location [m]. | +| entry_slices | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How many "slices" (beats or sub-beats) are allowed before reaching the desired pose. | +| exit_slices | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How many "slices" (beats or sub-beats) are allowed for the robot to return to the original pose. | + + + + + + + + +### BourreeParams + +Parameters for the Bourree move. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| velocity | [bosdyn.api.Vec2Value](#bosdyn.api.Vec2Value) | The speed at which we should bourree [m/s]. X is forward. Y is left. | +| yaw_rate | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How fast the bourree should turn [rad/s]. | +| stance_length | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How far apart front and hind feet should be. [m] | + + + + + + + + +### ButtCircleParams + +Parameters specific to the ButtCircle DanceMove. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| radius | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How big a circle the robutt will move in. Described in meters. | +| beats_per_circle | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The number of beats that elapse while performing the butt circle. | +| number_of_circles | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The number of circles that will be performed. If non-zero, takes precedence over beats_per_circle. | +| pivot | [Pivot](#bosdyn.api.spot.Pivot) | The pivot point the butt circles should be centered around. | +| clockwise | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Which way to rotate. | +| starting_angle | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Where to start. Zero is up. | + + + + + + + + +### ChickenHeadParams + +Parameters specific to the chicken head move. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| bob_magnitude | [bosdyn.api.Vec3Value](#bosdyn.api.Vec3Value) | Bobs the head in this direction in the robot footprint frame. | +| beats_per_cycle | [google.protobuf.Int32Value](#google.protobuf.Int32Value) | How fast to bob the head. | +| follow | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Should we move the frame when the robot steps? | + + + + + + + + +### ClapParams + +Parameters specific to clapping. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| direction | [bosdyn.api.Vec3Value](#bosdyn.api.Vec3Value) | Direction in a gravity-aligned body frame of clapping motion. A typical value for the location is (0, 1, 0). | +| location | [bosdyn.api.Vec3Value](#bosdyn.api.Vec3Value) | Location in body frame of the clap. A typical value for the location is (0.4, 0, -0.5). | +| speed | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Speed of the clap [m/s]. | +| clap_distance | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How far apart the limbs are before clapping [m]. | + + + + + + + + +### Color + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| red | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| green | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| blue | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | + + + + + + + + +### CrawlParams + +Parameters for the robot's crawling gait. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| swing_slices | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The number of slices (beats/sub-beats) the duration of a leg swing in the crawl gait should be. | +| velocity | [bosdyn.api.Vec2Value](#bosdyn.api.Vec2Value) | The speed at which we should crawl [m/s]. X is forward. Y is left. | +| stance_width | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The distance between the robot's left and right feet [m]. | +| stance_length | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The distance between the robot's front and back feet [m]. | + + + + + + + + +### EulerRateZYXValue + +Euler Angle rates (yaw->pitch->roll) vector that uses wrapped values so we can tell which elements are set. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| roll | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| pitch | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| yaw | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | + + + + + + + + +### EulerZYXValue + +Euler Angle (yaw->pitch->roll) vector that uses wrapped values so we can tell which elements are set. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| roll | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| pitch | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| yaw | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | + + + + + + + + +### FadeColorParams + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| top_color | [Color](#bosdyn.api.spot.Color) | | +| bottom_color | [Color](#bosdyn.api.spot.Color) | | +| fade_in_slices | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| fade_out_slices | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | + + + + + + + + +### FidgetStandParams + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| preset | [FidgetStandParams.FidgetPreset](#bosdyn.api.spot.FidgetStandParams.FidgetPreset) | | +| min_gaze_pitch | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| max_gaze_pitch | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| gaze_mean_period | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| gaze_center_cfp | [bosdyn.api.Vec3Value](#bosdyn.api.Vec3Value) | | +| shift_mean_period | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| shift_max_transition_time | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| breath_min_z | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| breath_max_z | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| breath_max_period | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| leg_gesture_mean_period | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| gaze_slew_rate | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| gaze_position_generation_gain | [bosdyn.api.Vec3Value](#bosdyn.api.Vec3Value) | | +| gaze_roll_generation_gain | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | + + + + + + + + +### Figure8Params + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| height | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| width | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| beats_per_cycle | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | + + + + + + + + +### FrameSnapshotParams + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| frame_id | [google.protobuf.Int32Value](#google.protobuf.Int32Value) | | +| fiducial_number | [google.protobuf.Int32Value](#google.protobuf.Int32Value) | | +| include_front_left_leg | [FrameSnapshotParams.Inclusion](#bosdyn.api.spot.FrameSnapshotParams.Inclusion) | | +| include_front_right_leg | [FrameSnapshotParams.Inclusion](#bosdyn.api.spot.FrameSnapshotParams.Inclusion) | | +| include_hind_left_leg | [FrameSnapshotParams.Inclusion](#bosdyn.api.spot.FrameSnapshotParams.Inclusion) | | +| include_hind_right_leg | [FrameSnapshotParams.Inclusion](#bosdyn.api.spot.FrameSnapshotParams.Inclusion) | | +| compensated | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | | + + + + + + + + +### FrontUpParams + +Parameters specific to FrontUp move. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| mirror | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Should we raise the hind feet instead. | + + + + + + + + +### GotoParams + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| absolute_position | [bosdyn.api.Vec2Value](#bosdyn.api.Vec2Value) | | +| absolute_yaw | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| step_position_stiffness | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| duty_cycle | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| link_to_next | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Should we combine with the next move into a smooth trajectory. | + + + + + + + + +### GripperParams + +Parameters for open/close of gripper. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| angle | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Angle in radians at which the gripper is open. Note that a 0 radian angle correlates to completely closed. | +| speed | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Speed in m/s at which the gripper should open/close to achieve the desired angle. | + + + + + + + + +### HopParams + +Parameters specific to Hop move. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| velocity | [bosdyn.api.Vec2Value](#bosdyn.api.Vec2Value) | The velocity of the hop gait (X is forward; y is left)[m/s]. | +| yaw_rate | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How fast the hop gait should turn [rad/s]. | +| stand_time | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How long the robot should stand in between each hop. | + + + + + + + + +### IndependentColorParams + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| top_left | [Color](#bosdyn.api.spot.Color) | | +| upper_mid_left | [Color](#bosdyn.api.spot.Color) | | +| lower_mid_left | [Color](#bosdyn.api.spot.Color) | | +| bottom_left | [Color](#bosdyn.api.spot.Color) | | +| top_right | [Color](#bosdyn.api.spot.Color) | | +| upper_mid_right | [Color](#bosdyn.api.spot.Color) | | +| lower_mid_right | [Color](#bosdyn.api.spot.Color) | | +| bottom_right | [Color](#bosdyn.api.spot.Color) | | +| fade_in_slices | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| fade_out_slices | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | + + + + + + + + +### JumpParams + +Parameters for the robot making a jump. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| yaw | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The amount in radians that the robot will turn while in the air. | +| flight_slices | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The amount of time in slices (beats) that the robot will be in the air. | +| stance_width | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The distance between the robot's left and right feet [m]. | +| stance_length | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The distance between the robot's front and back feet [m]. | +| translation | [bosdyn.api.Vec2Value](#bosdyn.api.Vec2Value) | How far the robot should jump [m]. | +| split_fraction | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How much it should lo/td the first pair of lets ahead of the other pair. In fraction of flight time. | +| lead_leg_pair | [JumpParams.Lead](#bosdyn.api.spot.JumpParams.Lead) | | +| yaw_is_absolute | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Should we turn to a yaw in choreography sequence frame? | +| translation_is_absolute | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Should we translate in choreography sequence frame? | +| absolute_yaw | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The direction the robot should face upon landing relative to pose at the start of the dance. [rad] | +| absolute_translation | [bosdyn.api.Vec2Value](#bosdyn.api.Vec2Value) | Where the robot should land relative to the pose at the start of the dance. [m] | +| swing_height | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| absolute | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Deprecation Warning *** DEPRECATED as of 3.0.0: The absolute field has been deprecated and split into the yaw_is_absolute and translation_is_absolute fields. The following field will be deprecated and moved to 'reserved' in a future release. | + + + + + + + + +### KneelCircleParams + +Parameters specific to the kneel_circles move. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| location | [bosdyn.api.Vec3Value](#bosdyn.api.Vec3Value) | Location in body frame of the circle center. A typical value for the location is (0.4, 0, -0.5). | +| beats_per_circle | [google.protobuf.Int32Value](#google.protobuf.Int32Value) | How beats per circle. One or two are reasonable values. | +| number_of_circles | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How many circles to perform. Mutually exclusive with beats_per_circle. | +| offset | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How far apart the feet are when circling [m]. | +| radius | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Size of the circles [m]. | +| reverse | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Which way to circle. | + + + + + + + + +### KneelLegMove2Params + +Parameters specific to KneelLegMove2 move. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| left_hip_x | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Joint angles of the front left leg in radians. | +| left_hip_y | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| left_knee | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| right_hip_x | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Joint angles of the front right leg in radians. | +| right_hip_y | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| right_knee | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| easing | [Easing](#bosdyn.api.spot.Easing) | How the motion should be paced. | +| link_to_next | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Should we combine with the next move into a smooth trajectory. | + + + + + + + + +### KneelLegMoveParams + +Parameters specific to KneelLegMove move. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| hip_x | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Joint angles of the left front leg in radians. If mirrored, the joints will be flipped for the other leg. | +| hip_y | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| knee | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| mirror | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | If mirrored is true, the joints will be flipped for the leg on the other side (right vs left) of the body. | +| easing | [Easing](#bosdyn.api.spot.Easing) | How the motion should be paced. | + + + + + + + + +### Pace2StepParams + +Parameters specific to pace translation. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| motion | [bosdyn.api.Vec2Value](#bosdyn.api.Vec2Value) | How far to move relative to starting position. [m] | +| absolute_motion | [bosdyn.api.Vec2Value](#bosdyn.api.Vec2Value) | Where to move relative to position at start of script. [m] | +| motion_is_absolute | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Is motion specified relative to pose at start of dance? | +| swing_height | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Swing parameters to describe the footstep pattern during the pace translation gait. Note, a zero (or nearly zero) value will be considered as an unspecified parameter. | +| swing_velocity | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| yaw | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How far to turn, described in radians with a positive value representing a turn to the left. | +| absolute_yaw | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Orientation to turn to, relative to the orientation at the start of the script. [rad] | +| yaw_is_absolute | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Should we turn to a yaw in choreography sequence frame? | +| absolute | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Deprecation Warning *** DEPRECATED as of 3.0.0: The absolute field has been deprecated and split into the yaw_is_absolute and translation_is_absolute fields. The following field will be deprecated and moved to 'reserved' in a future release. | + + + + + + + + +### RandomRotateParams + +Parameters specific to the RandomRotate move. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| amplitude | [EulerZYXValue](#bosdyn.api.spot.EulerZYXValue) | The amplitude [rad] of the rotation in each axis. | +| speed | [EulerRateZYXValue](#bosdyn.api.spot.EulerRateZYXValue) | The speed [rad/s] of the motion in each axis. | +| speed_variation | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The amount of variation allowed in the speed of the random rotations [m/s]. Note, this must be a positive value. | +| num_speed_tiers | [google.protobuf.Int32Value](#google.protobuf.Int32Value) | The specified speed values will be split into this many number of tiers between the bounds of [speed - speed_variation, speed + speed variation]. Then a tier (with a specified speed) will be randomly choosen and performed for each axis. | +| tier_variation | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How much can the output speed vary from the choosen tiered speed. | + + + + + + + + +### RippleColorParams + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| main | [Color](#bosdyn.api.spot.Color) | | +| secondary | [Color](#bosdyn.api.spot.Color) | | +| pattern | [RippleColorParams.Pattern](#bosdyn.api.spot.RippleColorParams.Pattern) | | +| light_side | [RippleColorParams.LightSide](#bosdyn.api.spot.RippleColorParams.LightSide) | | +| increment_slices | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | + + + + + + + + +### RotateBodyParams + +Parameters for the robot rotating the body. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| rotation | [EulerZYXValue](#bosdyn.api.spot.EulerZYXValue) | The robot will rotate its body to the specified orientation (roll/pitch/yaw). | +| return_to_start_pose | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | If true, the robot will transition back to the initial pose we started at before this choreography sequence move begin execution, and otherwise it will remain in whatever pose it is in after completing the choreography sequence move. | + + + + + + + + +### RunningManParams + +Parameters specific to RunningMan move. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| velocity | [bosdyn.api.Vec2Value](#bosdyn.api.Vec2Value) | | +| swing_height | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How high to pick up the forward-moving feet [m]. | +| spread | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How far to spread the contralateral pair of feet [m]. | +| reverse | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Should we reverse the motion? | +| pre_move_cycles | [google.protobuf.Int32Value](#google.protobuf.Int32Value) | How many full running man cycles should the robot complete in place before starting to move with the desired velocity. | +| speed_multiplier | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Do the move at some multiple of the dance cadence. | +| duty_cycle | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | What fraction of the time to have feet on the ground. | +| com_height | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How high to hold the center of mass above the ground on average. | + + + + + + + + +### SetColorParams + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| left_color | [Color](#bosdyn.api.spot.Color) | | +| right_same_as_left | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | | +| right_color | [Color](#bosdyn.api.spot.Color) | | +| fade_in_slices | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| fade_out_slices | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | + + + + + + + + +### SideParams + +Parameters for moves that can go to either side. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| side | [SideParams.Side](#bosdyn.api.spot.SideParams.Side) | | + + + + + + + + +### StepParams + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| foot | [Leg](#bosdyn.api.spot.Leg) | Which foot to use (FL = 1, FR = 2, HL = 3, HR = 4). | +| offset | [bosdyn.api.Vec2Value](#bosdyn.api.Vec2Value) | Offset of the foot from it's nominal position, in meters. | +| second_foot | [Leg](#bosdyn.api.spot.Leg) | Should we use a second foot? (None = 0, FL = 1, FR = 2, HL = 3, HR = 4). | +| swing_waypoint | [bosdyn.api.Vec3Value](#bosdyn.api.Vec3Value) | Where should the swing foot go? This vector should be described in a gravity-aligned body frame relative to the centerpoint of the swing. If set to {0,0,0}, uses the default swing path. | +| swing_height | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Parameters for altering swing. Note that these will have no effect if swing_waypoint is specified. As well, a zero (or nearly zero) value will be considered as an unspecified parameter. + +meters | +| liftoff_velocity | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | m/s | +| touchdown_velocity | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | m/s | +| mirror_x | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Should we mirror the offset for the second foot? Ignored if second_foot is set to None | +| mirror_y | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | | +| mirror | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Deprecation Warning *** DEPRECATED as of 2.3.0: The mirror field has been deprecated in favor for a more descriptive break down to mirror_x and mirror_y. The following field will be deprecated and moved to 'reserved' in a future release. | +| waypoint_dwell | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | What fraction of the swing should be spent near the waypoint. | +| touch | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Should we touch the ground and come back rather than stepping to a new place? | +| touch_offset | [bosdyn.api.Vec2Value](#bosdyn.api.Vec2Value) | | + + + + + + + + +### SwayParams + +Parameters specific to Sway move. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| vertical | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How far to move up/down [m]. | +| horizontal | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How far to move left/right [m]. | +| roll | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How much to roll [rad]. | +| pivot | [Pivot](#bosdyn.api.spot.Pivot) | What point on the robot's body should the swaying be centered at. For example, should the head move instead of the butt? | +| style | [SwayParams.SwayStyle](#bosdyn.api.spot.SwayParams.SwayStyle) | What style motion should we use? | +| pronounced | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How pronounced should the sway-style be? The value is on a scale from [0,1.0]. | +| hold_zero_axes | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Should the robot hold previous values for the vertical, horizontal, and roll axes if the value is left unspecified (value of zero). | + + + + + + + + +### TurnParams + +Parameters specific to turning. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| yaw | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How far to turn, described in radians with a positive value representing a turn to the left. | +| absolute_yaw | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Orientation to turn to, relative to the orientation at the start of the script. [rad] | +| yaw_is_absolute | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Should we turn to a yaw in choreography sequence frame? | +| swing_height | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Swing parameters to describe the footstep pattern during the turning [height in meters]. Note, a zero (or nearly zero) value will be considered as an unspecified parameter. | +| swing_velocity | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Swing parameter to describe the foot's swing velocity during the turning [m/s]. Note, a zero (or nearly zero) value will be considered as an unspecified parameter. | +| motion | [bosdyn.api.Vec2Value](#bosdyn.api.Vec2Value) | How far to move relative to starting position. [m] | +| absolute_motion | [bosdyn.api.Vec2Value](#bosdyn.api.Vec2Value) | Where to move relative to position at start of script. [m] | +| motion_is_absolute | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Is motion specified relative to pose at start of dance? | +| absolute | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Deprecation Warning *** DEPRECATED as of 3.0.0: The absolute field has been deprecated and split into the yaw_is_absolute and translation_is_absolute fields. The following field will be deprecated and moved to 'reserved' in a future release. | + + + + + + + + +### TwerkParams + +Parameters specific to twerking + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| height | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | How far the robot should twerk in meters. | + + + + + + + + +### WorkspaceArmMoveParams + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| rotation | [EulerZYXValue](#bosdyn.api.spot.EulerZYXValue) | The robot will rotate its body to the specified orientation (roll/pitch/yaw) [rad]. | +| translation | [bosdyn.api.Vec3Value](#bosdyn.api.Vec3Value) | The positional offset to the robot's current location [m]. | +| absolute | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Go to an absolute position/orientation? Otherwise, relative to starting pose. | +| frame | [ArmMoveFrame](#bosdyn.api.spot.ArmMoveFrame) | What frame is the motion specified in. | +| easing | [Easing](#bosdyn.api.spot.Easing) | How the motion should be paced. | +| dance_frame_id | [google.protobuf.Int32Value](#google.protobuf.Int32Value) | If we're using the dance frame, which one? | + + + + + + + + + + +### ArmMoveFrame + + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| ARM_MOVE_FRAME_UNKNOWN | 0 | | +| ARM_MOVE_FRAME_CENTER_OF_FOOTPRINT | 1 | | +| ARM_MOVE_FRAME_HAND | 2 | | +| ARM_MOVE_FRAME_BODY | 3 | | +| ARM_MOVE_FRAME_SHOULDER | 4 | | +| ARM_MOVE_FRAME_SHADOW | 5 | | +| ARM_MOVE_FRAME_DANCE | 6 | | + + + + + +### Easing + +Enum to describe the type of easing to perform for the slices at either (or both) the +beginning and end of a move. + + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| EASING_UNKNOWN | 0 | | +| EASING_LINEAR | 1 | | +| EASING_QUADRATIC_INPUT | 2 | | +| EASING_QUADRATIC_OUTPUT | 3 | | +| EASING_QUADRATIC_IN_OUT | 4 | | +| EASING_CUBIC_INPUT | 5 | | +| EASING_CUBIC_OUTPUT | 6 | | +| EASING_CUBIC_IN_OUT | 7 | | +| EASING_EXPONENTIAL_INPUT | 8 | | +| EASING_EXPONENTIAL_OUTPUT | 9 | | +| EASING_EXPONENTIAL_IN_OUT | 10 | | + + + + + +### FidgetStandParams.FidgetPreset + + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| PRESET_UNKNOWN | 0 | | +| PRESET_CUSTOM | 1 | | +| PRESET_INTEREST | 2 | | +| PRESET_PLAYFUL | 3 | | +| PRESET_FEAR | 4 | | +| PRESET_NERVOUS | 5 | | +| PRESET_EXHAUSTED | 6 | | + + + + + +### FrameSnapshotParams.Inclusion + + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| INCLUSION_UNKNOWN | 0 | | +| INCLUSION_IF_STANCE | 1 | | +| INCLUSION_INCLUDED | 2 | | +| INCLUSION_EXCLUDED | 3 | | + + + + + +### JumpParams.Lead + +If split_fraction is non-zero, which legs to lift first. + + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| LEAD_UNKNOWN | 0 | | +| LEAD_AUTO | 1 | | +| LEAD_FRONT | 2 | | +| LEAD_HIND | 3 | | +| LEAD_LEFT | 4 | | +| LEAD_RIGHT | 5 | | + + + + + +### LedLight + + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| LED_LIGHT_UNKNOWN | 0 | | +| LED_LIGHT_LEFT1 | 1 | | +| LED_LIGHT_LEFT2 | 2 | | +| LED_LIGHT_LEFT3 | 3 | | +| LED_LIGHT_LEFT4 | 4 | | +| LED_LIGHT_RIGHT1 | 5 | | +| LED_LIGHT_RIGHT2 | 6 | | +| LED_LIGHT_RIGHT3 | 7 | | +| LED_LIGHT_RIGHT4 | 8 | | + + + + + +### Leg + +Enum to describe which leg is being referenced in specific choreography sequence moves. + + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| LEG_UNKNOWN | 0 | | +| LEG_FRONT_LEFT | 1 | | +| LEG_FRONT_RIGHT | 2 | | +| LEG_HIND_LEFT | 3 | | +| LEG_HIND_RIGHT | 4 | | +| LEG_NO_LEG | -1 | | + + + + + +### Pivot + +Enum for the pivot point for certain choreography sequence moves. + + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| PIVOT_UNKNOWN | 0 | | +| PIVOT_FRONT | 1 | | +| PIVOT_HIND | 2 | | +| PIVOT_CENTER | 3 | | + + + + + +### RippleColorParams.LightSide + + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| LIGHT_SIDE_UNKNOWN | 0 | | +| LIGHT_SIDE_LEFT | 1 | | +| LIGHT_SIDE_RIGHT | 2 | | +| LIGHT_SIDE_BOTH_IN_SEQUENCE | 3 | | +| LIGHT_SIDE_BOTH_MATCHING | 4 | | + + + + + +### RippleColorParams.Pattern + + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| PATTERN_UNKNOWN | 0 | | +| PATTERN_FLASHING | 1 | | +| PATTERN_SNAKE | 2 | | +| PATTERN_ALTERNATE_COLORS | 3 | | +| PATTERN_FINE_GRAINED_ALTERNATE_COLORS | 4 | | + + + + + +### SideParams.Side + + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| SIDE_UNKNOWN | 0 | | +| SIDE_LEFT | 1 | | +| SIDE_RIGHT | 2 | | + + + + + +### SwayParams.SwayStyle + +The type of motion used by the Sway sequence move. + + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| SWAY_STYLE_UNKNOWN | 0 | | +| SWAY_STYLE_STANDARD | 1 | | +| SWAY_STYLE_FAST_OUT | 2 | | +| SWAY_STYLE_FAST_RETURN | 3 | | +| SWAY_STYLE_SQUARE | 4 | | +| SWAY_STYLE_SPIKE | 5 | | +| SWAY_STYLE_PLATEAU | 6 | | + + + + + + + + + + + + +# spot/choreography_sequence.proto + + + + + +### AnimateArm + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| joint_angles | [ArmJointAngles](#bosdyn.api.spot.ArmJointAngles) | Full arm joint angle specification. | +| hand_pose | [AnimateArm.HandPose](#bosdyn.api.spot.AnimateArm.HandPose) | The hand position in the animation frame | + + + + + + + + +### AnimateArm.HandPose + +An SE3 Pose for the hand where orientation is specified using either a quaternion or +euler angles + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| position | [bosdyn.api.Vec3Value](#bosdyn.api.Vec3Value) | | +| euler_angles | [EulerZYXValue](#bosdyn.api.spot.EulerZYXValue) | The hand's orientation described with euler angles (yaw, pitch, roll). | +| quaternion | [bosdyn.api.Quaternion](#bosdyn.api.Quaternion) | The hand's orientation described with a quaternion. | + + + + + + + + +### AnimateBody + +The AnimateBody keyframe describes the body's position and orientation. At least +one dimension of the body must be specified. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| body_pos | [bosdyn.api.Vec3Value](#bosdyn.api.Vec3Value) | The body position in the animation frame. | +| com_pos | [bosdyn.api.Vec3Value](#bosdyn.api.Vec3Value) | The body's center of mass position in the animation frame. | +| euler_angles | [EulerZYXValue](#bosdyn.api.spot.EulerZYXValue) | The body's orientation described with euler angles (yaw, pitch, roll). | +| quaternion | [bosdyn.api.Quaternion](#bosdyn.api.Quaternion) | The body's orientation described with a quaternion. | + + + + + + + + +### AnimateGripper + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| gripper_angle | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | + + + + + + + + +### AnimateLegs + +The AnimateLegs keyframe describes each leg using either joint angles or the foot position. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| fl | [AnimateSingleLeg](#bosdyn.api.spot.AnimateSingleLeg) | Front left leg. | +| fr | [AnimateSingleLeg](#bosdyn.api.spot.AnimateSingleLeg) | Front right leg. | +| hl | [AnimateSingleLeg](#bosdyn.api.spot.AnimateSingleLeg) | Hind left leg. | +| hr | [AnimateSingleLeg](#bosdyn.api.spot.AnimateSingleLeg) | Hind right leg. | + + + + + + + + +### AnimateSingleLeg + +A single leg keyframe to describe the leg motion. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| joint_angles | [LegJointAngles](#bosdyn.api.spot.LegJointAngles) | Full leg joint angle specification. | +| foot_pos | [bosdyn.api.Vec3Value](#bosdyn.api.Vec3Value) | The foot position of the leg in the animation frame. | +| stance | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | If true, the foot is in contact with the ground and standing. If false, the foot is in swing. If unset, the contact will be inferred from the leg joint angles or foot position. | + + + + + + + + +### Animation + +Represents an animated dance move that can be used whithin choreographies after uploading. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| name | [string](#string) | The name of the animated move, which is how it will be referenced in choreographies. | +| animation_keyframes | [AnimationKeyframe](#bosdyn.api.spot.AnimationKeyframe) | The animated move is composed of animation keyframes, which specify the duration of each frame. The keyframe describes the position of the body/arms/gripper. | +| controls_arm | [bool](#bool) | Indicators as to which parts of the robot that the move controls. | +| controls_legs | [bool](#bool) | | +| controls_body | [bool](#bool) | | +| controls_gripper | [bool](#bool) | | +| track_swing_trajectories | [bool](#bool) | Track animated swing trajectories. Otherwise, takes standard swings between animated liftoff and touchdown locations. | +| assume_zero_roll_and_pitch | [bool](#bool) | For moves that control the legs, but not the body. If legs are specified by joint angles, we still need body roll and pitch to know the foot height. If `assume_zero_roll_and_pitch` is true, they needn't be explicitly specified. | +| arm_playback | [Animation.ArmPlayback](#bosdyn.api.spot.Animation.ArmPlayback) | | +| bpm | [double](#double) | Optional bpm that the animation is successful at. | +| retime_to_integer_slices | [bool](#bool) | When true, rescales the time of each keyframe slightly such that the move takes an integer number of slices. If false/absent, the move will be padded or truncated slightly to fit an integer number of slices. | +| minimum_parameters | [AnimateParams](#bosdyn.api.spot.AnimateParams) | The different parameters (minimum, default, and maximum) that can change the move. The min/max bounds are used by Choreographer to constrain the parameter widget, and will also be used when uploading a ChoreographySequence containing the animation to validate that the animated move is allowed. | +| default_parameters | [AnimateParams](#bosdyn.api.spot.AnimateParams) | | +| maximum_parameters | [AnimateParams](#bosdyn.api.spot.AnimateParams) | | +| truncatable | [bool](#bool) | Indicates if the animated moves can be shortened (the animated move will be cut off). Not supported for leg moves. | +| extendable | [bool](#bool) | Indicates if the animated moves can be stretched (animated move will loop). Not supported for leg moves. | +| neutral_start | [bool](#bool) | Indicates if the move should start in a neutral stand position. | +| precise_steps | [bool](#bool) | Step exactly at the animated locations, even at the expense of balance. By default, the optimizer may adjust step locations slightly. | +| precise_timing | [bool](#bool) | Time everything exactly as animated, even at the expense of balance. By default, the optimizer may adjust timing slightly. | +| arm_required | [bool](#bool) | If set true, this animation will not run unless the robot has an arm. | +| arm_prohibited | [bool](#bool) | If set true, this animation will not run unless the robot has no arm. | +| no_looping | [bool](#bool) | If the animation completes before the move's duration, freeze rather than looping. | + + + + + + + + +### AnimationKeyframe + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| time | [double](#double) | Time from the start of the animation for this frame. | +| gripper | [AnimateGripper](#bosdyn.api.spot.AnimateGripper) | Different body parts the animated move can control. It can control multiple body parts at once. | +| arm | [AnimateArm](#bosdyn.api.spot.AnimateArm) | | +| body | [AnimateBody](#bosdyn.api.spot.AnimateBody) | | +| legs | [AnimateLegs](#bosdyn.api.spot.AnimateLegs) | | + + + + + + + + +### ArmJointAngles + +The AnimateArm keyframe describes the joint angles of the arm joints in radians. +Any joint not specified, will hold the previous angle it was at when the keyframe +begins. At least one arm joint must be specified. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| shoulder_0 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| shoulder_1 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| elbow_0 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| elbow_1 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| wrist_0 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | +| wrist_1 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | + + + + + + + + +### ChoreographerDisplayInfo + +Information for the Choreographer to display. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| color | [ChoreographerDisplayInfo.Color](#bosdyn.api.spot.ChoreographerDisplayInfo.Color) | | +| markers | [int32](#int32) | For the GUI, these are marked events in steps. For example if the move puts a foot down, the mark might be exactly when the foot is placed on the ground, relative to the start of the move. | +| description | [string](#string) | Textual description to be displayed in the GUI. | +| image | [string](#string) | Image path (local to the UI) to display as an icon. May be an animated gif. | +| category | [ChoreographerDisplayInfo.Category](#bosdyn.api.spot.ChoreographerDisplayInfo.Category) | | + + + + + + + + +### ChoreographerDisplayInfo.Color + +Color of the object. Set it to override the default category color. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| r | [int32](#int32) | RGB values for color ranging from [0,255]. | +| g | [int32](#int32) | | +| b | [int32](#int32) | | +| a | [double](#double) | Alpha value for the coloration ranges from [0,1]. | + + + + + + + + +### ChoreographerSave + +Describes the metadata and information only used by the Choreographer GUI, which isn't used in +the API + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| choreography_sequence | [ChoreographySequence](#bosdyn.api.spot.ChoreographySequence) | The main ChoreographySequence that makes up the dance and is sent to the robot. | +| music_file | [string](#string) | If specified this is the UI local path of the music to load. | +| music_start_slice | [double](#double) | UI specific member that describes exactly when the music should start, in slices. This is for time sync issues. | +| choreography_start_slice | [double](#double) | The start slice for the choreographer save. | + + + + + + + + +### ChoreographySequence + +Represents a particular choreography sequence, made up of MoveParams. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| name | [string](#string) | Display name or file name associated with the choreography sequence. | +| slices_per_minute | [double](#double) | Number of slices per minute in the choreography sequence. Typically a slice will correspond to 1/4 a beat. | +| moves | [MoveParams](#bosdyn.api.spot.MoveParams) | All of the moves in this choreography sequence. | + + + + + + + + +### ChoreographyStateLog + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| key_frames | [LoggedStateKeyFrame](#bosdyn.api.spot.LoggedStateKeyFrame) | A set of key frames recorded at a high rate. The key frames can be for the duration of an executing choreography or for the duration of a manual recorded log (triggered by the StartRecordingState and StopRecordingState RPCs). The specific set of keyframes is specified by the LogType when requesting to download the data. | + + + + + + + + +### DownloadRobotStateLogRequest + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header | +| log_type | [DownloadRobotStateLogRequest.LogType](#bosdyn.api.spot.DownloadRobotStateLogRequest.LogType) | Which data should we download. | + + + + + + + + +### DownloadRobotStateLogResponse + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header | +| status | [DownloadRobotStateLogResponse.Status](#bosdyn.api.spot.DownloadRobotStateLogResponse.Status) | Return status for the request. | +| chunk | [bosdyn.api.DataChunk](#bosdyn.api.DataChunk) | Chunk of data to download. Responses are sent in sequence until the data chunk is complete. After receiving all chunks, concatenate them into a single byte string. Then, deserialize the byte string into an ChoreographyStateLog object. | + + + + + + + + +### ExecuteChoreographyRequest + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header | +| choreography_sequence_name | [string](#string) | The string name of the ChoreographySequence to use. | +| start_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | The absolute time to start the choreography at. This should be in the robot's clock so we can synchronize music playing and the robot's choreography. | +| choreography_starting_slice | [double](#double) | The slice (betas/sub-beats) that the choreography should begin excution at. | +| lease | [bosdyn.api.Lease](#bosdyn.api.Lease) | The Lease to show ownership of the robot body. | + + + + + + + + +### ExecuteChoreographyResponse + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header | +| lease_use_result | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | | +| status | [ExecuteChoreographyResponse.Status](#bosdyn.api.spot.ExecuteChoreographyResponse.Status) | | + + + + + + + + +### LegJointAngles + +Descprition of each leg joint angle (hip x/y and knee) in radians. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| hip_x | [double](#double) | | +| hip_y | [double](#double) | | +| knee | [double](#double) | | + + + + + + + + +### ListAllMovesRequest + +Request a list of all possible moves and the associated parameters (min/max values). + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header | + + + + + + + + +### ListAllMovesResponse + +Response for ListAllMoves that defines the list of available moves and their parameter types. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header | +| moves | [MoveInfo](#bosdyn.api.spot.MoveInfo) | List of moves that the robot knows about. | +| move_param_config | [string](#string) | A copy of the MoveParamsConfig.txt that the robot is using. | + + + + + + + + +### LoggedFootContacts + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| fr_contact | [bool](#bool) | Boolean indicating whether or not the robot's foot is in contact with the ground. | +| fl_contact | [bool](#bool) | | +| hr_contact | [bool](#bool) | | +| hl_contact | [bool](#bool) | | + + + + + + + + +### LoggedJoints + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| fl | [LegJointAngles](#bosdyn.api.spot.LegJointAngles) | front left leg joint angles. | +| fr | [LegJointAngles](#bosdyn.api.spot.LegJointAngles) | front right leg joint angles. | +| hl | [LegJointAngles](#bosdyn.api.spot.LegJointAngles) | hind left leg joint angles. | +| hr | [LegJointAngles](#bosdyn.api.spot.LegJointAngles) | hind right leg joint angles. | +| arm | [ArmJointAngles](#bosdyn.api.spot.ArmJointAngles) | Full set of joint angles for the arm and gripper. | +| gripper_angle | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | | + + + + + + + + +### LoggedStateKeyFrame + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| joint_angles | [LoggedJoints](#bosdyn.api.spot.LoggedJoints) | Full set of joint angles for the robot. | +| foot_contact_state | [LoggedFootContacts](#bosdyn.api.spot.LoggedFootContacts) | Foot contacts for the robot. | +| animation_tform_body | [bosdyn.api.SE3Pose](#bosdyn.api.SE3Pose) | The current pose of the robot body in animation frame. The animation frame is defined based on the robot's footprint when the log first started recording. | +| timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | The timestamp (in robot time) for the key frame. | + + + + + + + + +### MoveInfo + +Defines properties of a choreography move. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| name | [string](#string) | Unique ID of the move type. | +| move_length_slices | [int32](#int32) | The duration of this move in slices (usually 1/4 beats). | +| move_length_time | [double](#double) | The duration of this move in seconds. If specified, overrides move_length_slices. | +| is_extendable | [bool](#bool) | If true, the duration may be adjusted from the default specified by move_length_slices or move_length_time. | +| min_move_length_slices | [int32](#int32) | Bounds on the duration may be adjusted in slices (usually 1/4 beats). These apply to extendable moves, but may also override move_length_time for some BPM. | +| max_move_length_slices | [int32](#int32) | | +| min_time | [double](#double) | Bounds on the duration in time. These apply to extendable moves, but may also override move_length_slices for some BPM. | +| max_time | [double](#double) | | +| entrance_states | [MoveInfo.TransitionState](#bosdyn.api.spot.MoveInfo.TransitionState) | The admissible states the robot can be in currently for this move to execute. | +| exit_state | [MoveInfo.TransitionState](#bosdyn.api.spot.MoveInfo.TransitionState) | The state of the robot after the move is complete. | +| controls_arm | [bool](#bool) | Indicators as to which parts of the robot that the move controls. | +| controls_legs | [bool](#bool) | | +| controls_body | [bool](#bool) | | +| controls_gripper | [bool](#bool) | | +| controls_lights | [bool](#bool) | | +| controls_annotations | [bool](#bool) | | +| display | [ChoreographerDisplayInfo](#bosdyn.api.spot.ChoreographerDisplayInfo) | Information for the GUI tool to visualize the sequence move info. | +| animated_move_generated_id | [google.protobuf.StringValue](#google.protobuf.StringValue) | Unique ID for the animated moves. This is sent with the UploadAnimatedMove request and use to track which version of the animated move is currently saved on robot. The ID can be unset, meaning the RPC which uploaded the animation did not provide an identifying hash. | + + + + + + + + +### MoveParams + +Defines varying parameters for a particular instance of a move. + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| type | [string](#string) | Unique ID of the move type that these params are associated with. | +| start_slice | [int32](#int32) | How many slices since the start of the song this move should be executed at. | +| requested_slices | [int32](#int32) | The number of slices (beats/sub-beats) that this move is supposed to last for. If the move was extendable, then this corresponds to the number of slices that the user requested. | +| jump_params | [JumpParams](#bosdyn.api.spot.JumpParams) | | +| rotate_body_params | [RotateBodyParams](#bosdyn.api.spot.RotateBodyParams) | | +| step_params | [StepParams](#bosdyn.api.spot.StepParams) | | +| butt_circle_params | [ButtCircleParams](#bosdyn.api.spot.ButtCircleParams) | | +| turn_params | [TurnParams](#bosdyn.api.spot.TurnParams) | | +| pace_2step_params | [Pace2StepParams](#bosdyn.api.spot.Pace2StepParams) | | +| twerk_params | [TwerkParams](#bosdyn.api.spot.TwerkParams) | | +| chicken_head_params | [ChickenHeadParams](#bosdyn.api.spot.ChickenHeadParams) | | +| clap_params | [ClapParams](#bosdyn.api.spot.ClapParams) | | +| front_up_params | [FrontUpParams](#bosdyn.api.spot.FrontUpParams) | | +| sway_params | [SwayParams](#bosdyn.api.spot.SwayParams) | | +| body_hold_params | [BodyHoldParams](#bosdyn.api.spot.BodyHoldParams) | | +| arm_move_params | [ArmMoveParams](#bosdyn.api.spot.ArmMoveParams) | | +| kneel_leg_move_params | [KneelLegMoveParams](#bosdyn.api.spot.KneelLegMoveParams) | | +| running_man_params | [RunningManParams](#bosdyn.api.spot.RunningManParams) | | +| kneel_circle_params | [KneelCircleParams](#bosdyn.api.spot.KneelCircleParams) | | +| gripper_params | [GripperParams](#bosdyn.api.spot.GripperParams) | | +| hop_params | [HopParams](#bosdyn.api.spot.HopParams) | | +| random_rotate_params | [RandomRotateParams](#bosdyn.api.spot.RandomRotateParams) | | +| crawl_params | [CrawlParams](#bosdyn.api.spot.CrawlParams) | | +| side_params | [SideParams](#bosdyn.api.spot.SideParams) | | +| bourree_params | [BourreeParams](#bosdyn.api.spot.BourreeParams) | | +| workspace_arm_move_params | [WorkspaceArmMoveParams](#bosdyn.api.spot.WorkspaceArmMoveParams) | | +| figure8_params | [Figure8Params](#bosdyn.api.spot.Figure8Params) | | +| kneel_leg_move2_params | [KneelLegMove2Params](#bosdyn.api.spot.KneelLegMove2Params) | | +| fidget_stand_params | [FidgetStandParams](#bosdyn.api.spot.FidgetStandParams) | | +| goto_params | [GotoParams](#bosdyn.api.spot.GotoParams) | | +| frame_snapshot_params | [FrameSnapshotParams](#bosdyn.api.spot.FrameSnapshotParams) | | +| set_color_params | [SetColorParams](#bosdyn.api.spot.SetColorParams) | | +| ripple_color_params | [RippleColorParams](#bosdyn.api.spot.RippleColorParams) | | +| fade_color_params | [FadeColorParams](#bosdyn.api.spot.FadeColorParams) | | +| independent_color_params | [IndependentColorParams](#bosdyn.api.spot.IndependentColorParams) | | +| animate_params | [AnimateParams](#bosdyn.api.spot.AnimateParams) | | + + + + + + + + +### StartRecordingStateRequest + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header | +| continue_recording_duration | [google.protobuf.Duration](#google.protobuf.Duration) | How long should the robot record for if no stop RPC is sent. A recording session can be extended by setting the recording_session_id below to a non-zero value matching the ID for the current recording session. For both start and continuation commands, the service will stop recording at end_time = (system time when the Start/Continue RPC is received) + (continue_recording_duration), unless another continuation request updates this end time. The robot has an internal maximum recording time of 5 minutes for the complete session log. | +| recording_session_id | [uint64](#uint64) | Provide the unique identifier of the recording session to extend the recording end time for. If the recording_session_id is 0, then it will create a new session and the robot will clear the recorded robot state buffer and restart recording. If this is a continuation of an existing recording session, than the robot will continue to record until the specified end time. | + + + + + + + + +### StartRecordingStateResponse + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header | +| status | [StartRecordingStateResponse.Status](#bosdyn.api.spot.StartRecordingStateResponse.Status) | | +| recording_session_id | [uint64](#uint64) | Unique identifier for the current recording session | + + + + + + + + +### StopRecordingStateRequest + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header | + + + + + + + + +### StopRecordingStateResponse + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header | + + + + + + + + +### UploadAnimatedMoveRequest + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header | +| animated_move_generated_id | [google.protobuf.StringValue](#google.protobuf.StringValue) | Unique ID for the animated moves. This will be automatically generated by the client and is used to uniquely identify the entire animation by creating a hash from the Animation protobuf message after serialization. The ID will be conveyed within the MoveInfo protobuf message in the ListAllMoves RPC. This ID allows the choreography client to only reupload animations that have changed or do not exist on robot already. | +| animated_move | [Animation](#bosdyn.api.spot.Animation) | AnimatedMove to upload to the robot and create a dance move from. | + + + + + + + + +### UploadAnimatedMoveResponse + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. | +| status | [UploadAnimatedMoveResponse.Status](#bosdyn.api.spot.UploadAnimatedMoveResponse.Status) | | +| warnings | [string](#string) | If the uploaded animated move is invalid (will throw a STATUS_ANIMATION_VALIDATION_FAILED), then warning messages describing the failure cases will be populated here to indicate which parts of the animated move failed. Note: there could be some warning messages even when an animation is marked as ok. | + + + + + + + + +### UploadChoreographyRequest + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header | +| choreography_sequence | [ChoreographySequence](#bosdyn.api.spot.ChoreographySequence) | ChoreographySequence to upload and store in memory | +| non_strict_parsing | [bool](#bool) | Should we run a script that has correctable errors? If true, the service will fix any correctable errors and run the corrected choreography script. If false, the service will reject a choreography script that has any errors. | + + + + + + + + +### UploadChoreographyResponse + + + +| Field | Type | Description | +| ----- | ---- | ----------- | +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. If the dance upload is invalid, the header INVALID request error will be set, which means that the choreography did not respect bounds of the parameters or has other attributes missing or incorrect. | +| warnings | [string](#string) | If the uploaded choreography is invalid (will throw a header InvalidRequest status), then certain warning messages will be populated here to indicate which choreography moves or parameters violated constraints of the robot. | + + + + + + + + + + +### Animation.ArmPlayback + +Mode for hand trajectory playback + + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| ARM_PLAYBACK_DEFAULT | 0 | Playback as specified. Arm animations specified with joint angles playback in jointspace and arm animations specified as hand poses playback in workspace. | +| ARM_PLAYBACK_JOINTSPACE | 1 | Playback in jointspace. Arm animation will be most consistent relative to the body | +| ARM_PLAYBACK_WORKSPACE | 2 | Playback in workspace. Hand pose animation will be most consistent relative to the current footprint. Reference frame is animation frame. | +| ARM_PLAYBACK_WORKSPACE_DANCE_FRAME | 3 | Playback in workspace with poses relative to the dance frame. hand pose animation will be most consistent relative to a fixed point in the world. | + + + + + +### ChoreographerDisplayInfo.Category + +Move Category affects the grouping in the choreographer list view, as well as the color it's +displayed with. + + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| CATEGORY_UNKNOWN | 0 | | +| CATEGORY_BODY | 1 | | +| CATEGORY_STEP | 2 | | +| CATEGORY_DYNAMIC | 3 | | +| CATEGORY_TRANSITION | 4 | | +| CATEGORY_KNEEL | 5 | | +| CATEGORY_ARM | 6 | | +| CATEGORY_ANIMATION | 7 | | +| CATEGORY_MPC | 8 | | +| CATEGORY_LIGHTS | 9 | | +| CATEGORY_ANNOTATIONS | 10 | | + + + + + +### DownloadRobotStateLogRequest.LogType + + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| LOG_TYPE_UNKNOWN | 0 | Unknown. Do not use. | +| LOG_TYPE_MANUAL | 1 | The robot state information recorded from the time of the manual start RPC (StartRecordingState) to either {the time of the manual stop RPC (StopRecordingState), the time of the download logs RPC, or the time of the internal service's buffer filling up}. | +| LOG_TYPE_LAST_CHOREOGRAPHY | 2 | The robot will automatically record robot state information for the entire duration of an executing choreography in addition to any manual logging. This log type will download this information for the last completed choreography. | + + + + + +### DownloadRobotStateLogResponse.Status + + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| STATUS_UNKNOWN | 0 | Status unknown. Do not use. | +| STATUS_OK | 1 | The log data downloaded successfully and is complete. | +| STATUS_NO_RECORDED_INFORMATION | 2 | Error where there is no robot state information logged in the choreography service. | +| STATUS_INCOMPLETE_DATA | 3 | Error where the complete duration of the recorded session caused the service's recording buffer to fill up. When full, the robot will stop recording but preserve whatever was recorded until that point. The robot has an internal maximum recording time of 5 minutes. The data streamed in this response will go from the start time until the time the buffer was filled. | + + + + + +### ExecuteChoreographyResponse.Status + + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| STATUS_UNKNOWN | 0 | | +| STATUS_OK | 1 | | +| STATUS_INVALID_UPLOADED_CHOREOGRAPHY | 2 | | +| STATUS_ROBOT_COMMAND_ISSUES | 3 | | +| STATUS_LEASE_ERROR | 4 | | + + + + + +### MoveInfo.TransitionState + +The state that the robot is in at the start or end of a move. + + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| TRANSITION_STATE_UNKNOWN | 0 | Unknown or unset state. | +| TRANSITION_STATE_STAND | 1 | The robot is in a normal (standing) state. | +| TRANSITION_STATE_KNEEL | 2 | The robot is kneeling down. | +| TRANSITION_STATE_SIT | 3 | The robot is sitting. | +| TRANSITION_STATE_SPRAWL | 4 | The robot requires a self-right. | + + + + + +### StartRecordingStateResponse.Status + +The status for the start recording request. + + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| STATUS_UNKNOWN | 0 | Status unknown; do not use. | +| STATUS_OK | 1 | The request succeeded and choreography has either started, or continued with an extended duration based on if a session_id was provided. | +| STATUS_UNKNOWN_RECORDING_SESSION_ID | 2 | The provided recording_session_id is unknown: it must either be 0 (start a new recording log) or it can match the current recording session id returned by the most recent start recording request. | +| STATUS_RECORDING_BUFFER_FULL | 3 | The Choreography Service's internal buffer is filled. It will record for a maximum of 5 minutes. It will stop recording, but save the recorded data until | + + + + + +### UploadAnimatedMoveResponse.Status + + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| STATUS_UNKNOWN | 0 | Do not use. | +| STATUS_OK | 1 | Uploading + parsing the animated move succeeded. | +| STATUS_ANIMATION_VALIDATION_FAILED | 2 | The animated move is considered invalid, see the warnings. | + + + + + + + + + + + + +# spot/choreography_service.proto + + + + + + + + + + + +### ChoreographyService + + + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| ListAllMoves | [ListAllMovesRequest](#bosdyn.api.spot.ListAllMovesRequest) | [ListAllMovesResponse](#bosdyn.api.spot.ListAllMovesResponse) | List the available dance moves and their parameter information. | +| UploadChoreography | [UploadChoreographyRequest](#bosdyn.api.spot.UploadChoreographyRequest) | [UploadChoreographyResponse](#bosdyn.api.spot.UploadChoreographyResponse) | Upload a dance to the robot. | +| UploadAnimatedMove | [UploadAnimatedMoveRequest](#bosdyn.api.spot.UploadAnimatedMoveRequest) | [UploadAnimatedMoveResponse](#bosdyn.api.spot.UploadAnimatedMoveResponse) | Upload an animation to the robot. | +| ExecuteChoreography | [ExecuteChoreographyRequest](#bosdyn.api.spot.ExecuteChoreographyRequest) | [ExecuteChoreographyResponse](#bosdyn.api.spot.ExecuteChoreographyResponse) | Execute the uploaded dance. | +| StartRecordingState | [StartRecordingStateRequest](#bosdyn.api.spot.StartRecordingStateRequest) | [StartRecordingStateResponse](#bosdyn.api.spot.StartRecordingStateResponse) | Manually start (or continue) recording the robot state. | +| StopRecordingState | [StopRecordingStateRequest](#bosdyn.api.spot.StopRecordingStateRequest) | [StopRecordingStateResponse](#bosdyn.api.spot.StopRecordingStateResponse) | Manually stop recording the robot state. | +| DownloadRobotStateLog | [DownloadRobotStateLogRequest](#bosdyn.api.spot.DownloadRobotStateLogRequest) | [DownloadRobotStateLogResponse](#bosdyn.api.spot.DownloadRobotStateLogResponse) stream | Download log of the latest recorded robot state information. | + + + + + + +# Standard Types + +| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby | +| ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- | +| double | | double | double | float | float64 | double | float | Float | +| float | | float | float | float | float32 | float | float | Float | +| int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) | +| uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) | +| sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) | +| fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum | +| sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| bool | | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass | +| string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) | +| bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) | + diff --git a/choreography_protos/bosdyn/api/spot/choreography_params.proto b/choreography_protos/bosdyn/api/spot/choreography_params.proto index 18d971af1..395c37d68 100644 --- a/choreography_protos/bosdyn/api/spot/choreography_params.proto +++ b/choreography_protos/bosdyn/api/spot/choreography_params.proto @@ -126,6 +126,7 @@ enum ArmMoveFrame { ARM_MOVE_FRAME_BODY = 3; ARM_MOVE_FRAME_SHOULDER = 4; ARM_MOVE_FRAME_SHADOW = 5; + ARM_MOVE_FRAME_DANCE = 6; } message WorkspaceArmMoveParams { @@ -143,6 +144,9 @@ message WorkspaceArmMoveParams { // How the motion should be paced. Easing easing = 5; + + // If we're using the dance frame, which one? + google.protobuf.Int32Value dance_frame_id = 6; } message Figure8Params { @@ -266,6 +270,15 @@ message CrawlParams { google.protobuf.DoubleValue stance_length = 4; } +message GotoParams { + Vec2Value absolute_position = 1; + google.protobuf.DoubleValue absolute_yaw = 2; + google.protobuf.DoubleValue step_position_stiffness = 3; + google.protobuf.DoubleValue duty_cycle = 4; + // Should we combine with the next move into a smooth trajectory. + google.protobuf.BoolValue link_to_next = 5; +} + // Parameters for the Bourree move. message BourreeParams { // The speed at which we should bourree [m/s]. X is forward. Y is left. @@ -296,8 +309,6 @@ message JumpParams { google.protobuf.DoubleValue stance_width = 3; // The distance between the robot's front and back feet [m]. google.protobuf.DoubleValue stance_length = 4; - // Should we turn to a yaw in choreography sequence frame? - google.protobuf.BoolValue absolute = 5; // How far the robot should jump [m]. Vec2Value translation = 6; // How much it should lo/td the first pair of lets ahead of the other pair. In fraction of flight time. @@ -312,6 +323,24 @@ message JumpParams { LEAD_RIGHT = 5; } Lead lead_leg_pair = 8; + + // Should we turn to a yaw in choreography sequence frame? + google.protobuf.BoolValue yaw_is_absolute = 11; + // Should we translate in choreography sequence frame? + google.protobuf.BoolValue translation_is_absolute = 12; + + // The direction the robot should face upon landing relative to pose at the start of the dance. [rad] + google.protobuf.DoubleValue absolute_yaw = 9; + // Where the robot should land relative to the pose at the start of the dance. [m] + Vec2Value absolute_translation = 10; + + google.protobuf.DoubleValue swing_height = 13; + + + // *** Deprecation Warning *** + // DEPRECATED as of 3.0.0: The absolute field has been deprecated and split into the yaw_is_absolute and translation_is_absolute fields. + // The following field will be deprecated and moved to 'reserved' in a future release. + google.protobuf.BoolValue absolute = 5 [deprecated = true]; } message StepParams { @@ -383,25 +412,51 @@ message TwerkParams { message TurnParams { // How far to turn, described in radians with a positive value representing a turn to the left. google.protobuf.DoubleValue yaw = 1; + // Orientation to turn to, relative to the orientation at the start of the script. [rad] + google.protobuf.DoubleValue absolute_yaw = 5; // Should we turn to a yaw in choreography sequence frame? - google.protobuf.BoolValue absolute = 2; + google.protobuf.BoolValue yaw_is_absolute = 6; // Swing parameters to describe the footstep pattern during the turning [height in meters]. Note, // a zero (or nearly zero) value will be considered as an unspecified parameter. google.protobuf.DoubleValue swing_height = 3; // Swing parameter to describe the foot's swing velocity during the turning [m/s]. Note, a zero // (or nearly zero) value will be considered as an unspecified parameter. google.protobuf.DoubleValue swing_velocity = 4; + // How far to move relative to starting position. [m] + Vec2Value motion = 7; + // Where to move relative to position at start of script. [m] + Vec2Value absolute_motion = 8; + // Is motion specified relative to pose at start of dance? + google.protobuf.BoolValue motion_is_absolute = 9; + + // *** Deprecation Warning *** + // DEPRECATED as of 3.0.0: The absolute field has been deprecated and split into the yaw_is_absolute and translation_is_absolute fields. + // The following field will be deprecated and moved to 'reserved' in a future release. + google.protobuf.BoolValue absolute = 2 [deprecated = true]; } // Parameters specific to pace translation. message Pace2StepParams { - // Where to move. + // How far to move relative to starting position. [m] Vec2Value motion = 1; + // Where to move relative to position at start of script. [m] + Vec2Value absolute_motion = 6; + // Is motion specified relative to pose at start of dance? + google.protobuf.BoolValue motion_is_absolute = 7; // Swing parameters to describe the footstep pattern during the pace translation gait. Note, a zero (or nearly zero) // value will be considered as an unspecified parameter. google.protobuf.DoubleValue swing_height = 3; google.protobuf.DoubleValue swing_velocity = 4; - // Should the motion be relative to where the dance started (true) rather than relative to the current position (false). + // How far to turn, described in radians with a positive value representing a turn to the left. + google.protobuf.DoubleValue yaw = 8; + // Orientation to turn to, relative to the orientation at the start of the script. [rad] + google.protobuf.DoubleValue absolute_yaw = 9; + // Should we turn to a yaw in choreography sequence frame? + google.protobuf.BoolValue yaw_is_absolute = 10; + + // *** Deprecation Warning *** + // DEPRECATED as of 3.0.0: The absolute field has been deprecated and split into the yaw_is_absolute and translation_is_absolute fields. + // The following field will be deprecated and moved to 'reserved' in a future release. google.protobuf.BoolValue absolute = 5; } @@ -448,3 +503,167 @@ message FrontUpParams { google.protobuf.BoolValue mirror = 1; } +message FidgetStandParams { + enum FidgetPreset { + PRESET_UNKNOWN = 0; + PRESET_CUSTOM = 1; + PRESET_INTEREST = 2; + PRESET_PLAYFUL = 3; + PRESET_FEAR = 4; + PRESET_NERVOUS = 5; + PRESET_EXHAUSTED = 6; + } + FidgetPreset preset = 1; + google.protobuf.DoubleValue min_gaze_pitch = 2; + google.protobuf.DoubleValue max_gaze_pitch = 3; + google.protobuf.DoubleValue gaze_mean_period = 4; + Vec3Value gaze_center_cfp = 5; + google.protobuf.DoubleValue shift_mean_period = 6; + google.protobuf.DoubleValue shift_max_transition_time = 7; + google.protobuf.DoubleValue breath_min_z = 8; + google.protobuf.DoubleValue breath_max_z = 9; + google.protobuf.DoubleValue breath_max_period = 10; + google.protobuf.DoubleValue leg_gesture_mean_period = 11; + google.protobuf.DoubleValue gaze_slew_rate = 12; + Vec3Value gaze_position_generation_gain = 13; + google.protobuf.DoubleValue gaze_roll_generation_gain = 14; +} + +message FrameSnapshotParams { + google.protobuf.Int32Value frame_id = 1; + google.protobuf.Int32Value fiducial_number = 2; + + enum Inclusion { + INCLUSION_UNKNOWN = 0; + INCLUSION_IF_STANCE = 1; + INCLUSION_INCLUDED = 2; + INCLUSION_EXCLUDED = 3; + } + Inclusion include_front_left_leg = 3; + Inclusion include_front_right_leg = 4; + Inclusion include_hind_left_leg = 5; + Inclusion include_hind_right_leg = 6; + google.protobuf.BoolValue compensated = 7; +} + +message SetColorParams { + Color left_color = 1; + google.protobuf.BoolValue right_same_as_left = 2; + Color right_color = 3; + google.protobuf.DoubleValue fade_in_slices = 4; + google.protobuf.DoubleValue fade_out_slices = 5; +} + +message FadeColorParams { + Color top_color = 1; + Color bottom_color = 2; + google.protobuf.DoubleValue fade_in_slices = 3; + google.protobuf.DoubleValue fade_out_slices = 4; +} + +message IndependentColorParams { + Color top_left = 1; + Color upper_mid_left = 2; + Color lower_mid_left = 3; + Color bottom_left = 4; + Color top_right = 5; + Color upper_mid_right = 6; + Color lower_mid_right = 7; + Color bottom_right = 8; + google.protobuf.DoubleValue fade_in_slices = 9; + google.protobuf.DoubleValue fade_out_slices = 10; +} + +enum LedLight { + LED_LIGHT_UNKNOWN = 0; + LED_LIGHT_LEFT1 = 1; + LED_LIGHT_LEFT2 = 2; + LED_LIGHT_LEFT3 = 3; + LED_LIGHT_LEFT4 = 4; + LED_LIGHT_RIGHT1 = 5; + LED_LIGHT_RIGHT2 = 6; + LED_LIGHT_RIGHT3 = 7; + LED_LIGHT_RIGHT4 = 8; +} + +message Color { + google.protobuf.DoubleValue red = 1; + google.protobuf.DoubleValue green = 2; + google.protobuf.DoubleValue blue = 3; +} + +message RippleColorParams { + Color main = 1; + Color secondary = 2; + + enum Pattern { + PATTERN_UNKNOWN = 0; + PATTERN_FLASHING = 1; + PATTERN_SNAKE = 2; + PATTERN_ALTERNATE_COLORS = 3; + PATTERN_FINE_GRAINED_ALTERNATE_COLORS = 4; + } + Pattern pattern = 3; + + enum LightSide { + LIGHT_SIDE_UNKNOWN = 0; + LIGHT_SIDE_LEFT = 1; + LIGHT_SIDE_RIGHT = 2; + LIGHT_SIDE_BOTH_IN_SEQUENCE = 3; + LIGHT_SIDE_BOTH_MATCHING = 4; + } + LightSide light_side = 4; + google.protobuf.DoubleValue increment_slices = 5; +} + + +message AnimateParams { + // The name of the animated move. There are no default values/bounds associated with this field. + string animation_name = 1; + + // How many slices to smoothly transition from previous pose to animation. + google.protobuf.DoubleValue body_entry_slices = 2; + + // How many slices to return from animation to nominal pose. Zero indicates to keep final animated pose. + google.protobuf.DoubleValue body_exit_slices = 3; + + // Multiplier for animated translation by axis to exaggerate or suppress motion along specific axes. + Vec3Value translation_multiplier = 4; + + // Multiplier for the animated orientation by axis to exaggerate or suppress motion along specific axes. + EulerZYXValue rotation_multiplier = 5; + + // How many slices to smoothly transition from previous pose to animation. + google.protobuf.DoubleValue arm_entry_slices = 6; + + // Joint angle offsets in radians for the arm joints. + google.protobuf.DoubleValue shoulder_0_offset = 7; + google.protobuf.DoubleValue shoulder_1_offset = 8; + google.protobuf.DoubleValue elbow_0_offset = 9; + google.protobuf.DoubleValue elbow_1_offset = 10; + google.protobuf.DoubleValue wrist_0_offset = 11; + google.protobuf.DoubleValue wrist_1_offset = 12; + google.protobuf.DoubleValue gripper_offset = 13; + + // How fast to playback. 1.0 is normal speed. larger is faster. + google.protobuf.DoubleValue speed = 14; + + // How late into the nominal script to start. + google.protobuf.DoubleValue offset_slices = 15; + + // Multiply all gripper angles by this value. + google.protobuf.DoubleValue gripper_multiplier = 16; + + // How hard the gripper can squeeze. Fraction of full strength. + google.protobuf.DoubleValue gripper_strength_fraction = 17; + + // Which dance frame to use as a reference for workspace arm moves. Including this parameter + // overrides the animation frame. + google.protobuf.Int32Value arm_dance_frame_id = 18; + + // How hard to try to track the animated body motion. + // Only applicable to animations that control both the body and the legs. + // On a scale of 1 to 10 (11 for a bit extra). + // Higher will result in more closely tracking the animated body motion, but possibly at the expense of balance for more difficult animations. + google.protobuf.DoubleValue body_tracking_stiffness = 19; +} diff --git a/choreography_protos/bosdyn/api/spot/choreography_sequence.proto b/choreography_protos/bosdyn/api/spot/choreography_sequence.proto index fba074c19..b1798332f 100644 --- a/choreography_protos/bosdyn/api/spot/choreography_sequence.proto +++ b/choreography_protos/bosdyn/api/spot/choreography_sequence.proto @@ -10,10 +10,14 @@ package bosdyn.api.spot; option java_outer_classname = "ChoreographyProto"; -import "bosdyn/api/header.proto"; +import "google/protobuf/duration.proto"; import "google/protobuf/timestamp.proto"; +import "google/protobuf/wrappers.proto"; +import "bosdyn/api/geometry.proto"; +import "bosdyn/api/header.proto"; import "bosdyn/api/lease.proto"; import "bosdyn/api/spot/choreography_params.proto"; +import "bosdyn/api/data_chunk.proto"; // Request a list of all possible moves and the associated parameters (min/max values). message ListAllMovesRequest { @@ -26,7 +30,7 @@ message ListAllMovesResponse { // Common response header ResponseHeader header = 1; - // List of moves that the robot knows about + // List of moves that the robot knows about. repeated MoveInfo moves = 2; // A copy of the MoveParamsConfig.txt that the robot is using. @@ -58,6 +62,39 @@ message UploadChoreographyResponse { repeated string warnings = 3; } +message UploadAnimatedMoveRequest { + // Common request header + RequestHeader header = 1; + + // Unique ID for the animated moves. This will be automatically generated by the client + // and is used to uniquely identify the entire animation by creating a hash from the Animation + // protobuf message after serialization. The ID will be conveyed within the MoveInfo protobuf + // message in the ListAllMoves RPC. This ID allows the choreography client to only reupload + // animations that have changed or do not exist on robot already. + google.protobuf.StringValue animated_move_generated_id = 3; + + // AnimatedMove to upload to the robot and create a dance move from. + Animation animated_move = 2; +} + +message UploadAnimatedMoveResponse { + // Common response header. + ResponseHeader header = 1; + + enum Status { + STATUS_UNKNOWN = 0; // Do not use. + STATUS_OK = 1; // Uploading + parsing the animated move succeeded. + STATUS_ANIMATION_VALIDATION_FAILED = 2; // The animated move is considered invalid, see the warnings. + } + Status status = 2; + + // If the uploaded animated move is invalid (will throw a STATUS_ANIMATION_VALIDATION_FAILED), then + // warning messages describing the failure cases will be populated here to indicate which + // parts of the animated move failed. Note: there could be some warning messages even when an animation + // is marked as ok. + repeated string warnings = 3; +} + message ExecuteChoreographyRequest { // Common request header RequestHeader header = 1; @@ -91,6 +128,151 @@ message ExecuteChoreographyResponse { Status status = 3; } +message StartRecordingStateRequest { + // Common request header + RequestHeader header = 1; + + // How long should the robot record for if no stop RPC is sent. A recording session can be + // extended by setting the recording_session_id below to a non-zero value matching the ID for the + // current recording session. + // For both start and continuation commands, the service will stop recording at + // end_time = (system time when the Start/Continue RPC is received) + (continue_recording_duration), + // unless another continuation request updates this end time. + // The robot has an internal maximum recording time of 5 minutes for the complete session log. + google.protobuf.Duration continue_recording_duration = 2; + + // Provide the unique identifier of the recording session to extend the recording end time for. + // If the recording_session_id is 0, then it will create a new session and the robot will clear + // the recorded robot state buffer and restart recording. + // If this is a continuation of an existing recording session, than the robot will continue + // to record until the specified end time. + uint64 recording_session_id = 3; +} + +message StartRecordingStateResponse { + // Common response header + ResponseHeader header = 1; + + // The status for the start recording request. + enum Status { + // Status unknown; do not use. + STATUS_UNKNOWN = 0; + // The request succeeded and choreography has either started, or continued with an extended + // duration based on if a session_id was provided. + STATUS_OK = 1; + // The provided recording_session_id is unknown: it must either be 0 (start a new recording log) + // or it can match the current recording session id returned by the most recent start recording request. + STATUS_UNKNOWN_RECORDING_SESSION_ID = 2; + // The Choreography Service's internal buffer is filled. It will record for a maximum of 5 minutes. It + // will stop recording, but save the recorded data until + STATUS_RECORDING_BUFFER_FULL = 3; + } + Status status = 2; + + // Unique identifier for the current recording session + uint64 recording_session_id = 3; +} + +message StopRecordingStateRequest { + // Common request header + RequestHeader header = 1; +} + +message StopRecordingStateResponse { + // Common response header + ResponseHeader header = 1; +} + +message DownloadRobotStateLogRequest { + // Common request header + RequestHeader header = 1; + + enum LogType { + // Unknown. Do not use. + LOG_TYPE_UNKNOWN = 0; + // The robot state information recorded from the time of the manual start RPC (StartRecordingState) + // to either {the time of the manual stop RPC (StopRecordingState), the time of the download logs RPC, + // or the time of the internal service's buffer filling up}. + LOG_TYPE_MANUAL = 1; + // The robot will automatically record robot state information for the entire duration of an executing + // choreography in addition to any manual logging. This log type will download this information for the + // last completed choreography. + LOG_TYPE_LAST_CHOREOGRAPHY = 2; + } + // Which data should we download. + LogType log_type = 2; +} + +message LoggedJoints { + LegJointAngles fl = 1; // front left leg joint angles. + LegJointAngles fr = 2; // front right leg joint angles. + LegJointAngles hl = 3; // hind left leg joint angles. + LegJointAngles hr = 4; // hind right leg joint angles. + + // Full set of joint angles for the arm and gripper. + ArmJointAngles arm = 5; + google.protobuf.DoubleValue gripper_angle = 6; +} + +message LoggedFootContacts { + // Boolean indicating whether or not the robot's foot is in contact with the ground. + bool fr_contact = 1; + bool fl_contact = 2; + bool hr_contact = 3; + bool hl_contact = 4; +} + +message LoggedStateKeyFrame { + // Full set of joint angles for the robot. + LoggedJoints joint_angles = 1; + + // Foot contacts for the robot. + LoggedFootContacts foot_contact_state = 4; + + // The current pose of the robot body in animation frame. The animation frame is defined + // based on the robot's footprint when the log first started recording. + SE3Pose animation_tform_body = 2; + + // The timestamp (in robot time) for the key frame. + google.protobuf.Timestamp timestamp = 3; +} + +message ChoreographyStateLog { + // A set of key frames recorded at a high rate. The key frames can be for the duration of an executing + // choreography or for the duration of a manual recorded log (triggered by the StartRecordingState and + // StopRecordingState RPCs). The specific set of keyframes is specified by the LogType when requesting + // to download the data. + repeated LoggedStateKeyFrame key_frames = 1; +} + +message DownloadRobotStateLogResponse { + // Common response header + ResponseHeader header = 1; + + enum Status { + // Status unknown. Do not use. + STATUS_UNKNOWN = 0; + // The log data downloaded successfully and is complete. + STATUS_OK = 1; + // Error where there is no robot state information logged in the choreography service. + STATUS_NO_RECORDED_INFORMATION = 2; + // Error where the complete duration of the recorded session caused the service's recording + // buffer to fill up. When full, the robot will stop recording but preserve whatever was + // recorded until that point. The robot has an internal maximum recording time of 5 minutes. + // The data streamed in this response will go from the start time until the time the buffer + // was filled. + STATUS_INCOMPLETE_DATA = 3; + } + // Return status for the request. + Status status = 2; + + // Chunk of data to download. Responses are sent in sequence until the + // data chunk is complete. After receiving all chunks, concatenate them + // into a single byte string. Then, deserialize the byte string into an + // ChoreographyStateLog object. + DataChunk chunk = 3; +} + // Defines varying parameters for a particular instance of a move. message MoveParams { // Unique ID of the move type that these params are associated with. @@ -131,7 +313,16 @@ message MoveParams { WorkspaceArmMoveParams workspace_arm_move_params = 33; Figure8Params figure8_params = 34; KneelLegMove2Params kneel_leg_move2_params = 35; + FidgetStandParams fidget_stand_params = 36; + GotoParams goto_params = 37; + FrameSnapshotParams frame_snapshot_params = 38; + SetColorParams set_color_params = 39; + RippleColorParams ripple_color_params = 40; + FadeColorParams fade_color_params = 41; + IndependentColorParams independent_color_params = 42; + + AnimateParams animate_params = 1000; } } @@ -140,14 +331,26 @@ message MoveInfo { // Unique ID of the move type. string name = 1; - // The number of "slices" (beats or sub-beats) that this move takes up. + // The duration of this move in slices (usually 1/4 beats). int32 move_length_slices = 2; + // The duration of this move in seconds. If specified, overrides move_length_slices. + double move_length_time = 15; + + // If true, the duration may be adjusted from the default specified by move_length_slices or move_length_time. + bool is_extendable = 3; - // The minimum number of "slices" that this move can complete in. + // Bounds on the duration may be adjusted in slices (usually 1/4 beats). + // These apply to extendable moves, but may also override move_length_time for some BPM. int32 min_move_length_slices = 13; + int32 max_move_length_slices = 14; - // If true, the user can extend the move beyond the requested length. - bool is_extendable = 3; + // Bounds on the duration in time. + // These apply to extendable moves, but may also override move_length_slices for some BPM. + double min_time = 6; + double max_time = 7; + + // If the slice bounds and time bounds do not overlap for a particular bpm, + // the duration will be set to the minimum, violating the specified maximum. // The state that the robot is in at the start or end of a move. enum TransitionState { @@ -162,18 +365,22 @@ message MoveInfo { // The state of the robot after the move is complete. TransitionState exit_state = 5; - // The absolute minimum and maximum times of the move in seconds. - double min_time = 6; - double max_time = 7; // Indicators as to which parts of the robot that the move controls. bool controls_arm = 8; bool controls_legs = 9; bool controls_body = 10; bool controls_gripper = 12; + bool controls_lights = 17; + bool controls_annotations = 18; // Information for the GUI tool to visualize the sequence move info. ChoreographerDisplayInfo display = 11; + + // Unique ID for the animated moves. This is sent with the UploadAnimatedMove request and use + // to track which version of the animated move is currently saved on robot. The ID can be unset, + // meaning the RPC which uploaded the animation did not provide an identifying hash. + google.protobuf.StringValue animated_move_generated_id = 16; } // Information for the Choreographer to display. @@ -210,6 +417,9 @@ message ChoreographerDisplayInfo { CATEGORY_KNEEL = 5; CATEGORY_ARM = 6; CATEGORY_ANIMATION = 7; + CATEGORY_MPC = 8; + CATEGORY_LIGHTS = 9; + CATEGORY_ANNOTATIONS = 10; } Category category = 16; } @@ -239,4 +449,208 @@ message ChoreographerSave { // UI specific member that describes exactly when the music should start, in slices. This is for // time sync issues. double music_start_slice = 3; + + // The start slice for the choreographer save. + double choreography_start_slice = 4; +} + +// Represents an animated dance move that can be used whithin choreographies after uploading. +message Animation { + // The name of the animated move, which is how it will be referenced in choreographies. + string name = 1; + + // The animated move is composed of animation keyframes, which specify the duration of + // each frame. The keyframe describes the position of the body/arms/gripper. + repeated AnimationKeyframe animation_keyframes = 2; + + // Indicators as to which parts of the robot that the move controls. + bool controls_arm = 3; + bool controls_legs = 4; + bool controls_body = 5; + bool controls_gripper = 6; + + // Track animated swing trajectories. Otherwise, takes standard swings between animated liftoff and touchdown locations. + bool track_swing_trajectories = 16; + + // For moves that control the legs, but not the body. + // If legs are specified by joint angles, we still need body roll and pitch to know the foot height. + // If `assume_zero_roll_and_pitch` is true, they needn't be explicitly specified. + bool assume_zero_roll_and_pitch = 19; + + // Mode for hand trajectory playback + enum ArmPlayback { + // Playback as specified. Arm animations specified with joint angles playback in jointspace + // and arm animations specified as hand poses playback in workspace. + ARM_PLAYBACK_DEFAULT = 0; + // Playback in jointspace. Arm animation will be most consistent relative to the body + ARM_PLAYBACK_JOINTSPACE = 1; + // Playback in workspace. Hand pose animation will be most consistent relative to the + // current footprint. Reference frame is animation frame. + ARM_PLAYBACK_WORKSPACE = 2; + // Playback in workspace with poses relative to the dance frame. hand pose animation will be + // most consistent relative to a fixed point in the world. + ARM_PLAYBACK_WORKSPACE_DANCE_FRAME = 3; + } + ArmPlayback arm_playback = 17; + + // Optional bpm that the animation is successful at. + double bpm = 7; + + // When true, rescales the time of each keyframe slightly such that the move takes an + // integer number of slices. If false/absent, the move will be padded or truncated slightly + // to fit an integer number of slices. + bool retime_to_integer_slices = 8; + + // The different parameters (minimum, default, and maximum) that can change the move. + // The min/max bounds are used by Choreographer to constrain the parameter widget, and will + // also be used when uploading a ChoreographySequence containing the animation to validate + // that the animated move is allowed. + AnimateParams minimum_parameters = 9; + AnimateParams default_parameters = 10; + AnimateParams maximum_parameters = 11; + + // Indicates if the animated moves can be shortened (the animated move will be cut off). Not + // supported for leg moves. + bool truncatable = 12; + + // Indicates if the animated moves can be stretched (animated move will loop). Not supported for + // leg moves. + bool extendable = 13; + + // Indicates if the move should start in a neutral stand position. + bool neutral_start = 14; + + // Step exactly at the animated locations, even at the expense of balance. + // By default, the optimizer may adjust step locations slightly. + bool precise_steps = 15; + + // Time everything exactly as animated, even at the expense of balance. + // By default, the optimizer may adjust timing slightly. + bool precise_timing = 18; + + // If set true, this animation will not run unless the robot has an arm. + bool arm_required = 20; + + // If set true, this animation will not run unless the robot has no arm. + bool arm_prohibited = 22; + + // If the animation completes before the move's duration, freeze rather than looping. + bool no_looping = 21; +} + +message AnimationKeyframe { + // Time from the start of the animation for this frame. + double time = 1; + + // Different body parts the animated move can control. + // It can control multiple body parts at once. + AnimateGripper gripper = 2; + AnimateArm arm = 3; + AnimateBody body = 4; + AnimateLegs legs = 5; +} + +message AnimateGripper { + google.protobuf.DoubleValue gripper_angle = 1; +} + +message AnimateArm { + // An SE3 Pose for the hand where orientation is specified using either a quaternion or + // euler angles + message HandPose { + Vec3Value position = 1; + + oneof orientation { + // The hand's orientation described with euler angles (yaw, pitch, roll). + EulerZYXValue euler_angles = 3; + + // The hand's orientation described with a quaternion. + Quaternion quaternion = 4; + } + } + + // For the animated arm, the arm can be described using either the joint angles or + // the pose of the hand. NOTE: each keyframe within a single Animation proto must always + // specify the arm using the same format for all frames. + oneof arm { + // Full arm joint angle specification. + ArmJointAngles joint_angles = 1; + + // The hand position in the animation frame + HandPose hand_pose = 2; + } +} + +// The AnimateArm keyframe describes the joint angles of the arm joints in radians. +// Any joint not specified, will hold the previous angle it was at when the keyframe +// begins. At least one arm joint must be specified. +message ArmJointAngles { + google.protobuf.DoubleValue shoulder_0 = 1; + google.protobuf.DoubleValue shoulder_1 = 2; + google.protobuf.DoubleValue elbow_0 = 3; + google.protobuf.DoubleValue elbow_1 = 4; + google.protobuf.DoubleValue wrist_0 = 5; + google.protobuf.DoubleValue wrist_1 = 6; +} + +// The AnimateBody keyframe describes the body's position and orientation. At least +// one dimension of the body must be specified. +message AnimateBody { + + // For the animated body keyframe, describe the body position using either the body position or + // the center of mass position. NOTE: each keyframe within a single Animation proto must always + // specify the body position using the same format for all frames. + oneof position { + // The body position in the animation frame. + Vec3Value body_pos = 1; + + // The body's center of mass position in the animation frame. + Vec3Value com_pos = 2; + } + + // For the animated body keyframe, describe the body orientation using either euler angles or a + // quaternion. NOTE: each keyframe within a single Animation proto must always + // specify the body orientation using the same format for all frames. + oneof orientation { + // The body's orientation described with euler angles (yaw, pitch, roll). + EulerZYXValue euler_angles = 3; + + // The body's orientation described with a quaternion. + Quaternion quaternion = 4; + } +} + +// The AnimateLegs keyframe describes each leg using either joint angles or the foot position. +message AnimateLegs { + AnimateSingleLeg fl = 1; // Front left leg. + AnimateSingleLeg fr = 2; // Front right leg. + AnimateSingleLeg hl = 3; // Hind left leg. + AnimateSingleLeg hr = 4; // Hind right leg. +} + +// A single leg keyframe to describe the leg motion. +message AnimateSingleLeg { + + // For the animated single legs, the leg can be described using either the joint angles or + // the position of the foot. NOTE: each keyframe within a single Animation proto must always + // specify a single leg using the same format for all frames. + oneof leg { + // Full leg joint angle specification. + LegJointAngles joint_angles = 1; + + // The foot position of the leg in the animation frame. + Vec3Value foot_pos = 2; + } + + // If true, the foot is in contact with the ground and standing. If false, the + // foot is in swing. If unset, the contact will be inferred from the leg joint angles + // or foot position. + google.protobuf.BoolValue stance = 3; +} + +// Descprition of each leg joint angle (hip x/y and knee) in radians. +message LegJointAngles { + double hip_x = 1; + double hip_y = 2; + double knee = 3; } diff --git a/choreography_protos/bosdyn/api/spot/choreography_service.proto b/choreography_protos/bosdyn/api/spot/choreography_service.proto index de31a53cc..8a1100723 100644 --- a/choreography_protos/bosdyn/api/spot/choreography_service.proto +++ b/choreography_protos/bosdyn/api/spot/choreography_service.proto @@ -19,6 +19,18 @@ service ChoreographyService { // Upload a dance to the robot. rpc UploadChoreography(UploadChoreographyRequest) returns (UploadChoreographyResponse) {} + // Upload an animation to the robot. + rpc UploadAnimatedMove(UploadAnimatedMoveRequest) returns (UploadAnimatedMoveResponse) {} + // Execute the uploaded dance. rpc ExecuteChoreography(ExecuteChoreographyRequest) returns (ExecuteChoreographyResponse) {} + + // Manually start (or continue) recording the robot state. + rpc StartRecordingState(StartRecordingStateRequest) returns (StartRecordingStateResponse) {} + + // Manually stop recording the robot state. + rpc StopRecordingState(StopRecordingStateRequest) returns (StopRecordingStateResponse) {} + + // Download log of the latest recorded robot state information. + rpc DownloadRobotStateLog(DownloadRobotStateLogRequest) returns (stream DownloadRobotStateLogResponse) {} } diff --git a/docs/concepts/README.md b/docs/concepts/README.md index aa7b62498..892bfdb3e 100644 --- a/docs/concepts/README.md +++ b/docs/concepts/README.md @@ -32,6 +32,7 @@ Finally, payloads allow for expansion of services beyond those provided by Spot * [Geometry and Frames](geometry_and_frames.md) * [Robot services](robot_services.md) * [E-Stop](estop_service.md) +* [Lease](lease_service.md) * [Developing API Services](developing_api_services.md) * [Faults](faults.md) * [Autonomy services](autonomy/README.md) diff --git a/docs/concepts/arm/arm_services.md b/docs/concepts/arm/arm_services.md index d15babeca..e8f982685 100644 --- a/docs/concepts/arm/arm_services.md +++ b/docs/concepts/arm/arm_services.md @@ -37,9 +37,10 @@ Requests an end effector trajectory move while applying some force to the ground A [GCODE](https://en.wikipedia.org/wiki/G-code) interpreter that can be used to draw with sidewalk chalk. ## Door Service -The door service is a framework for opening doors. We provide two command types: -* **Auto** This message requires users to specify a location to search for a door handle along with and some door parameters. Spot autonomously grabs the handle, opens the door, and walks through. +The door service is a framework for opening doors. We support three command types: +* **AutoGrasp**: This message requires users to specify a location to search for a door handle along with and some door parameters. Spot autonomously grabs the handle, opens the door, and walks through. * **Warmstart**: In Warmstart, the assumption is the robot is already grasping the door handle. The robot will skip the grasp stage of auto, and immediately begin opening the door and then traverses through the doorway. +* **AutoPush**: Used for doors that can be opened via a push without requiring a grasp. This includes pushbars, crashbars, and doors without a latching mechanism. The robot will point the hand down and push the door open with its wrist based on a push point supplied over the API. See the following example for using this service: diff --git a/docs/concepts/autonomy/README.md b/docs/concepts/autonomy/README.md index e6eac8d4b..e24732c8f 100644 --- a/docs/concepts/autonomy/README.md +++ b/docs/concepts/autonomy/README.md @@ -30,3 +30,4 @@ The Autowalk feature is an implementation of the autonomous navigation API. Howe * [Missions service](missions_service.md) * [Network compute bridge](../network_compute_bridge.md) * [AutoReturn service](auto_return.md) +* [Directed Exploration](directed_exploration.md) diff --git a/docs/concepts/autonomy/directed_exploration.md b/docs/concepts/autonomy/directed_exploration.md new file mode 100644 index 000000000..e876b8519 --- /dev/null +++ b/docs/concepts/autonomy/directed_exploration.md @@ -0,0 +1,43 @@ + + +# Directed Exploration + +## What is Directed Exploration? + +Directed Exploration is a feature that enables Spot to navigate robustly when the environment has changed. When Spot's path is blocked by an unexpected obstacle, Directed Exploration allows Spot to use its sensors to find a clear path to a nearby waypoint, even if that waypoint is not connected to Spot's current waypoint in the GraphNav map. This enables Spot to deal with environments where new obstacles may have been added, old obstacles may have been moved, and doors may have been opened or closed. + +For example, suppose you record the Autowalk mission shown below, where Spot visits the waypoints 1 through 6 in order. When you record the mission, door A is open and door B is closed. + +![Initial recorded path](./images/directed_exploration_1.png "Initial recorded path") + +Now, imagine someone closes door A and opens door B. Without Directed Exploration, Spot would get stuck at closed door A when it tried to replay the mission. + +![Changed environment](./images/directed_exploration_2.png "Changed environment") + +Directed Exploration will see that Spot can travel from waypoint 2 to waypoint 5 through open door B, even though this door was closed when the mission was recorded. + +![Directed Exploration path](./images/directed_exploration_3.png "Directed Exploration path") + +After reaching waypoint 5, Spot will navigate to the final destination at waypoint 6. + +## When is Directed Exploration invoked? + +Directed Exploration is invoked as a last resort when all other attempts to find a path to the destination have failed. Other strategies include attempting to plan a different route through the edges in the GraphNav map and attempting to follow alternate waypoints that are created next to the waypoints on the map. + +Directed Exploration can succeed in some situations where these other strategies fail, because Directed Exploration frees Spot from the constaint of navigating along edges stored in the GraphNav map. Directed Exploration allows Spot to use its sensors to determine whether it can reach nearby waypoints, even if those waypoints aren't connected to its current location in the GraphNav map. + +## How to enable/disable Directed Exploration + +Directed Exploration is enabled by default. + +To disable Directed Exploration when replaying an Autowalk mission from the tablet, uncheck the box marked "Drive around large obstacles" on the mission upload screen. This will disable both Directed Exploration and alternate waypoints, but it will still allow replanning alternate routes through the GraphNav map. + +To disable Directed Exploration for a NavigateTo or NavigateRoute command issued through the SDK, set the disable_directed_exploration field of the corresponding TravelParams proto to true. + +To disable Directed Exploration for a mission requested through the SDK, set the disable_directed_exploration field of the PlaySettings proto in the PlayMissionRequest to true. diff --git a/docs/concepts/autonomy/graphnav_map_structure.md b/docs/concepts/autonomy/graphnav_map_structure.md index 395e7b40d..71574f455 100644 --- a/docs/concepts/autonomy/graphnav_map_structure.md +++ b/docs/concepts/autonomy/graphnav_map_structure.md @@ -25,6 +25,12 @@ The viewer also shows fiducials that were detected during the map recording proc Maps do not have a global coordinate system (like GPS coordinates, for example). Only the relative transformations between waypoints are known. +# Recording and Modifying Maps + +The GraphNav map recording service is used to create and modify maps using robot data. Using the `StartRecording` RPC, you can tell the map recording service to begin creating a new map. You may then drive the robot around a site, and it will record a map. Afterwards (or at intervals during recording), you may download map data. + +The [`recording_command_line` example](../../../python/examples/graph_nav_command_line/README.md#recording-service-command-line) in the Spot SDK shows how to record and modify maps at runtime, and how to download map data from the recording service. + ## Creating waypoints Waypoints are created by the robot every two meters during map recording. However, they can be created in areas where the robot’s path has more curvature as it navigates around corners and obstacles. @@ -32,6 +38,128 @@ Waypoints are created by the robot every two meters during map recording. Howeve Call the `CreateWaypoint` RPC at specific locations in order to perform some action in a mission or when you want to explicitly maintain some mapping between a user event and a waypoint ID. Example: creating a waypoint at an inspection point. +## Creating edges + +Edges are created automatically while recording is active. One edge will be created between every new waypoint that the recording service creates. + +If you want to create edges manually, you can call the `CreateEdge` RPC in the recording service. + + +# Map Processing +Starting in the 3.0 SDK release, the GraphNav map processing service makes two new options available to modify maps, Topology Processing (automatic loop closure) and Anchoring Optimization. The map processing service is an RPC service interface which shares a map with the GraphNav Service. The map processing Service performs operations on the GraphNav map, and can either return results to the user, or write results into the shared map. The map processing service may be used after recording a map using the Map Recording Service. + +The [`recording_command_line` example](../../../python/examples/graph_nav_command_line/README.md#recording-service-command-line) has examples for how to use the map processing service on a newly recorded GraphNav map. + +## Topology Processing +The Topology of a GraphNav map refers to the connectivity between different Waypoints using Edges. Below are some examples of Maps with different topologies. + +![Topologies](./images/topologies.png) + +The recording service records one kind of map topology: the Chain. Chains always go from a starting point to an ending point. Chains are easy to create -- it only requires recording the robot’s current position and sensor data relative to the previous pose and sensor data. There are no connections between waypoints that aren’t along the chain, even if they happen to cross each other in physical space. + +When starting a new recording while the robot is localized to an existing waypoint, the recording service will create a Branch -- which is simply a Chain starting from a waypoint within another Chain. + +Loops are topologies where there is more than one possible path between two waypoints. The recording service is **unable to create loops** on its own. To create loops, you can manually call the `CreateEdge` RPC, or, starting in SDK 3.0, you may use the map processing service to automatically close loops. + +### Why are loops important? +Any time the robot crosses its own path in physical space, there is an opportunity to create a topological connection (an edge) between where the robot currently is, and where it was in the past. This is called *"loop closure"*. + +![The robot has returned to the start](./images/robot_back_at_start.png) + +For example, if the robot has returned to its starting place after traversing hundreds of meters through a site, it would be desirable to know that the start and end points of the map are topologically connected. + +For example, consider the chain: + +``` +start waypoint -> (1,000 waypoints) -> end waypoint +``` + +The robot may only travel along edges while using GraphNav, so without an edge between the starting and ending waypoints of the map, the robot is unable to travel betwen the start and end directly. For example, the command: + +``` +NavigateTo(start waypoint) +``` +causes the robot to traverse the 1,000 waypoints it had recorded *in reverse* rather than walking directly from the end to the start. + +To prevent this, we must **close the loop** between the starting and ending waypoint. + +## Automatic Loop Closure +You can issue a `ProcessTopologyRequest` to the map processing service to automatically close loops in the GraphNav map. This helps the robot know which areas of the map are topologically connected so that it can make smarter decisions about initializing its localization to the map and planning around it. + +Note that Loop Closures in the Autonomy SDK affect both localization and navigation -- an edge is expected to be traversable by the robot in a physical sense (that is, the robot can walk from point A to point B if there is an edge between A and B), and it also encodes the relative transformation between the waypoints it connects. + + +![Topology processing example](./images/topo_processing_example.svg) + +There are two types of loop closure currently supported: odometry loop closures and fiducial loop closures. + +**Odometry loop closures** occur whenever: +* The robot walks a total path length of less than 50 meters (for base platform robots) or 100 meters (for robots with LIDAR) and believes it is back at the same location (within 4 meters). +* That path **does not cross a staircase**. +* The resulting loop closure edge does not cross any body obstacles. + +To close larger loops, the map processing service relies on fiducials. **Fiducial loop closures** occur whenever: + +* The robot sees the same fiducial twice. +* In both instances that the robot sees the fiducial, the fiducial was less than 4 meters away and had low measurement error. +* The resulting loop closure edge does not cross any body obstacles. + +> Note: the Autowalk app automatically calls topology processing after recording ends. SDK users must make sure to manually call the map processing service to enable this feature. See the recording_command_line.py example for how to call this service. + +### Ensuring good topology + +To make sure that the map processing service has the best chance of closing loops, adhere to the following guidelines: + +* The more fiducials in the environment, the better. +* If the map is multi-floor, there must be at least one fiducial per floor. +* Try to pass as closely as possible to fiducials while recording. +* Try to record multiple pathways through the same site to maximize the chance that the robot will be able to navigate around obstacles. + +# Anchorings and Anchoring Optimization +The 3.0 SDK introduces a new concept for GraphNav maps called *anchorings*. In addition to waypoints and edges, GraphNav maps now have an attached anchoring for waypoints and fiducials. + +An *anchoring* is a mapping from waypoints to some global reference frame. That is, for every waypoint and fiducial, we have an `SE3Pose` describing the transform from a *seed* frame to that waypoint or fiducial. + +For example, to get all of the positions of the waypoints relative to the seed frame, you can do this: + +```python +# For each waypoint in the graph's anchoring, prints the (x, y, z) position of that waypoint. +def print_anchorings(graph): + for anchor in graph.anchoring.anchors: + pos = anchor.seed_tform_waypoint.position + print("id: {} x: {} y: {} z: {}".format(anchor.id, pos.x, pos.y, pos.z)) +``` +> Note: it is not necessary for every waypoint in the graph to have an anchoring. GraphNav will use edge transformations to interpolate between the provided anchors. + +All GraphNav maps have a *default anchoring*, which comes from the User Origin if it exists, or the starting waypoint otherwise. The default anchoring is computed by extrapolating robot odometry over the edges in the graph. + +Additionally, the map processing tool can be used to **optimize the anchoring** via the `ProcessAnchoringRequest` RPC. An example for how to use this RPC using its default parameters can be found in the [`recording_command_line` example](../../../python/examples/graph_nav_command_line/README.md#recording-service-command-line). A more complex example can be found in the [`graph_nav_anchoring_optimization` SDK example](../../../python/examples/graph_nav_anchoring_optimization/README.md). + +Anchoring optimization can be used to improve the **metric consistency** of a map's anchoring. An example is shown below: + +![Anchoring before optimization](./images/treehouse_ko.png) +*A real, 1km graph_nav map displayed from above before anchoring optimization is applied. The long red and yellow lines are loop closures from topology processing. The numbers are instances of fiducials stored in individual waypoints.* + +![Anchoring after optimization](./images/treehouse_anchoring.png) +*The same map after anchoring optimization has been applied. Notice that segments of the building which were disjoint before now align.* + +> Note: the Autowalk app automatically calls anchoring optimization after recording ends. SDK users must make sure to manually call the anchoring optimization RPC to access this feature. + +## What can you do with anchorings? + +Anchorings can be used to describe the global relationships and layout between waypoints in a GraphNav map. The pose of the robot relative to the anchoring is also available in the Localization message returned by GraphNav as `seed_tform_body`. + +Anchorings may also be used to display the GraphNav map relative to a blueprint, BIM model, or other pre-existing map. For details on how to achieve this, please see the [`graph_nav_anchoring_optimization` SDK example](../../../python/examples/graph_nav_anchoring_optimization/README.md). + +There is also an SDK example for how to use GraphNav anchorings to extract a global 3D point cloud from a map and save it as a PLY file. This is located in the [`graph_nav_extract_point_cloud` SDK example.](../../../python/examples/graph_nav_extract_point_cloud/README.md) + +![An extracted point cloud of a real GraphNav map](./images/extract_cloud.png) *An extracted point cloud from a real GraphNav map, viewed in a 3rdparty tool called [CloudCompare](https://www.danielgm.net/cc/).* + +Starting in SDK release 3.0, anchorings can also be used to command the robot. The `NavigateToAnchor` command can be used to send the robot to an approximate `x, y, z` position relative to the seed frame. To do this, the robot will navigate over a series of waypoints and edges from its current location to the nearest waypoint to the given commanded pose, and will then walk in a straight line toward the commanded pose. + +# Map Data Transfer +The GraphNav service has one active map instance on the robot that it shares with the GraphNav recording service. You can download the data stored in this map instance, or upload new data to the robot to replace or extend the current map instance. + ## Downloading maps Waypoints and edges have associated snapshots that store data the robot uses to compute localization and inform the robot’s locomotion. diff --git a/docs/concepts/autonomy/images/directed_exploration_1.png b/docs/concepts/autonomy/images/directed_exploration_1.png new file mode 100644 index 000000000..f9b4e3023 Binary files /dev/null and b/docs/concepts/autonomy/images/directed_exploration_1.png differ diff --git a/docs/concepts/autonomy/images/directed_exploration_2.png b/docs/concepts/autonomy/images/directed_exploration_2.png new file mode 100644 index 000000000..11b857852 Binary files /dev/null and b/docs/concepts/autonomy/images/directed_exploration_2.png differ diff --git a/docs/concepts/autonomy/images/directed_exploration_3.png b/docs/concepts/autonomy/images/directed_exploration_3.png new file mode 100644 index 000000000..37162a86d Binary files /dev/null and b/docs/concepts/autonomy/images/directed_exploration_3.png differ diff --git a/docs/concepts/autonomy/images/extract_cloud.png b/docs/concepts/autonomy/images/extract_cloud.png new file mode 100644 index 000000000..f08180399 Binary files /dev/null and b/docs/concepts/autonomy/images/extract_cloud.png differ diff --git a/docs/concepts/autonomy/images/robot_back_at_start.png b/docs/concepts/autonomy/images/robot_back_at_start.png new file mode 100644 index 000000000..bea92c405 Binary files /dev/null and b/docs/concepts/autonomy/images/robot_back_at_start.png differ diff --git a/docs/concepts/autonomy/images/topo_processing_example.svg b/docs/concepts/autonomy/images/topo_processing_example.svg new file mode 100644 index 000000000..63d4b09e6 --- /dev/null +++ b/docs/concepts/autonomy/images/topo_processing_example.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/concepts/autonomy/images/topologies.png b/docs/concepts/autonomy/images/topologies.png new file mode 100644 index 000000000..3da1fe508 Binary files /dev/null and b/docs/concepts/autonomy/images/topologies.png differ diff --git a/docs/concepts/autonomy/images/treehouse_anchoring.png b/docs/concepts/autonomy/images/treehouse_anchoring.png new file mode 100644 index 000000000..8670996c4 Binary files /dev/null and b/docs/concepts/autonomy/images/treehouse_anchoring.png differ diff --git a/docs/concepts/autonomy/images/treehouse_ko.png b/docs/concepts/autonomy/images/treehouse_ko.png new file mode 100644 index 000000000..8613538a1 Binary files /dev/null and b/docs/concepts/autonomy/images/treehouse_ko.png differ diff --git a/docs/concepts/autonomy/initialization.md b/docs/concepts/autonomy/initialization.md index bacf172bd..9b7d2829b 100644 --- a/docs/concepts/autonomy/initialization.md +++ b/docs/concepts/autonomy/initialization.md @@ -74,7 +74,7 @@ Note that areas with intersecting walls, corners, furniture, equipment, and othe ## Initializing with search -If fiducials aren’t available, the client program can use other methods of initialization through the `SetLocalization` RPC (available in the [`graph_nav.proto`](../../../protos/bosdyn/api/graph_nav/graph_nav.proto)). The client can provide an initial guess for the complete localization in the `initial_guess` field which describes the robot’s pose relative to a known waypoint and which waypoint to initialize to. Details on the algorithm that is run when a initial guess is provided are described in the next section. +If fiducials are not available, the client program can use other methods of initialization through the `SetLocalization` RPC (available in the [`graph_nav.proto`](../../../protos/bosdyn/api/graph_nav/graph_nav.proto)). The client can provide an initial guess for the complete localization in the `initial_guess` field which describes the robot’s pose relative to a known waypoint and which waypoint to initialize to. Details on the algorithm that is run when a initial guess is provided are described in the next section. If the initial guess for the localization is unknown, then GraphNav can perform a brute force search. The parameters of that search are set in the `SetLocalization` RPC via `max_distance` and `max_yaw` fields. Depending on the size of these parameters, the `SetLocalization` RPC can take a long time to complete. diff --git a/docs/concepts/base_services.md b/docs/concepts/base_services.md index d8113ffbf..a47559357 100644 --- a/docs/concepts/base_services.md +++ b/docs/concepts/base_services.md @@ -105,3 +105,10 @@ This estimate is purely at the application layer. This is important for two rea Clients needing high precision timing, such as a payload sensor collecting data while the robot is moving, should instead use NTP to synchronize to the robot. +## lease + +The lease service provides methods to achieve ownership over controlling the robot and maintaining valid communication with that owner. + +The lease is required to issue commands that control the robots mobility and motion, such as powering on the robot or commanding stand. To start, the lease must be acquired (or taken) to show ownership of the robot resources. Then retain lease signals must be sent throughout the duration of the operation to preserve ownership and indicate reliable communication with the lease owner. Ultimately the lease should be returned to free the resources to be reclaimed; however, it can be revoked during operation by another user or if the communication signals are not received within a certain period of time. + +The [Lease documentation](lease_service.md) provides a more in-depth description of leases, typical lease usage, and how to understand lease errors. \ No newline at end of file diff --git a/docs/concepts/choreography/README.md b/docs/concepts/choreography/README.md index f9214bc36..bdfc9787a 100644 --- a/docs/concepts/choreography/README.md +++ b/docs/concepts/choreography/README.md @@ -20,6 +20,9 @@ The high-level [documentation](choreography_service.md) provides an overview of ## Contents * [Choreography Service](choreography_service.md) +* [Animations in Choreography](animations_in_choreographer.md) * [Move Reference Guide](move_reference.md) * [Choreographer Setup](choreographer_setup.md) -* [Choreographer Overview](choreographer.md) \ No newline at end of file +* [Choreographer Overview](choreographer.md) +* [Robot Connections in Choreographer](robot_controls_in_choreographer.md) +* [Animation File Format](animation_file_specification.md) \ No newline at end of file diff --git a/docs/concepts/choreography/animation_file_specification.md b/docs/concepts/choreography/animation_file_specification.md new file mode 100644 index 000000000..01856c165 --- /dev/null +++ b/docs/concepts/choreography/animation_file_specification.md @@ -0,0 +1,220 @@ + + +# Animation Files for Choreographer + +Choreographer supports animations that are parsed from a specific file format into an `Animation` protobuf message, which will be uploaded to the robot using the `UploadAnimation` RPC. Animation files are human readable/editable text files with a *.cha extension. The animation file format consists of three sections: the options section, the parameters section, and the key frames section. + +There are three methods to parse an animation cha file into an `Animation` protobuf message: +1. Create an animations folder in the same directory as the Choreographer executable. All *.cha animation files within this folder will be automatically parsed and uploaded to robot when Choreographer is opened. The directory structure will look like this: + +``` +dance_directory/ + choreographer.exe + animations/ + bourree_arm.cha + my_animation.cha +``` + +2. In Choreographer, click “File” -> “Load Animated Move” to upload a single animation file after the application is already opened. +3. The python script `animation_file_to_proto.py` (in the bosdyn-choreography-client package) will parse the text file and can be used to output a protobuf message. + + +# File Specification + +## File Name and Extension +The animation text file will have the *.cha extension. The filename will become the move name, which can be referenced in choreography sequences. Choreographer will display the animated move name with underscores converted to spaces and each word capitalized. + +## Structure +The animation cha file consists of three sections. Each section must be present and separated by a blank line such that the animation file parser can succeed. In all three sections, values within the same line can be separated by any combination of spaces and tabs. + +The file sections are: +- **Options:** pre-defined information used by both Choreographer and the robot to interpret the animation correctly. +- **Parameters:** adjustable values to customize the animated move further. These values appear in the Parameters section of Choreographer. +- **Body Keyframes:** the complete set of key frames, either densely or sparsely specified, for a fixed amount of time. The move duration/keyframe timestamps are indicated by either a timestamp in this section, or computed from the frequency defined in the options section). The keyframes consist of either joint angles or poses for the body parts being controlled. + +## Units +All units are in: +- Distance: meters +- Angles: radians +- Time: seconds. Sometimes time is measured in “slices” (¼ beat), so the duration is dependent on the sequence's BPM (beats per minute). + +## Commenting Support +We support comments within the animation files. A comment is marked with either “#” or “//” and can take up an entire line in the file or be at the end of an existing line. Comments must be within one of the three main sections of the file, and they cannot create a new section. + +# Options File Section +The options section defines how the animated move is controlled and interpreted by both Choreographer and the robot. The section consists of lines with a keyword at the beginning of the line, and a fixed number of values separated by spaces following the keyword. The number of values is specific to the keyword used and defined below. Some keywords, like “neutral_start”, have no values following the keyword. + +The options section must contain a definition for which tracks the animation controls. This is specified by the “controls” keyword, and a line structured as follows: +``` +controls TRACK1... TRACK_N +``` +Following the keyword "controls", the fields TRACK1 … TRACK_N are one or more of [legs, body, arm, gripper] (e.g. `controls legs body` for a move that controls the legs and body but not the arm or gripper.) + +## Supported Keywords for the Options Section +Other than the “controls” tracks keyword, the other keywords in the options section are optional. The following are the supported key words and defining values for this section and what they define about the animated move: + +`bpm VALUE`: Where `VALUE` is the nominal beats per minute of the animation. If the script is at a different BPM, the animation will be time-scaled so it takes the same number of beats. If not specified, the animation will play at the nominal speed regardless of script BPM, taking a variable number of beats. + +`extendable`: Indicates the move can be stretched in Choreographer. Doing so will cause the animation to loop. + +`truncatable`: Indicates that the move can be shortened in Choreographer. Doing so will cause the animation to end early. + +`display_rgb RED GREEN BLUE`: Where `RED`, `GREEN`, `BLUE` are integer numbers between 0 and 255. This defines the color the move block will display as within Choreographer. If not present, a default color will be generated based on the hash of the file name. + +`frequency HZ`: Where `HZ` is the number of frames per second in the animation body. If unspecified, the time column must be present in the body. + +`retime_to_integer_slices`: Rescales time slightly so that the move takes an integer number of slices. If absent, the animation will be padded or truncated slightly to take an integer number of slices. + +`description TEXT`: Where `TEXT` to be displayed within Choreographer as a description of the animation. + +`neutral_start`: Applies only to moves that control the body but do not contain leg information. Indicates the move can be assumed to start in a neutral stand. If not specified, it is instead assumed that the center of footprint is at the origin of the animation frame. + +`precise_steps`: Step exactly at the animated locations, even at the expense of balance. + +`precise_timing`: Step exactly at the animated times, even at the expense of balance. + +`track_swing_trajectories`: Track animated swing trajectories. Otherwise, takes standard swings between animated liftoff and touchdown locations. + +`arm_playback OPTION`: Where `OPTION` is one of the following. If `OPTION` is not included the default behavior is for arm animations specified as joint angles to playback as joint space and animations specified as hand poses to default to workspace. + +- `jointspace`: Arm animation playback is with respect to the body. +- `workspace`: Arm animation playback is with respect to the current footprint. +- `workspace_dance_frame`: Arm animation playback is with respect to a dance frame in workspace. Parameter arm_dance_frame_id can be used to specify which dance frame if multiple exist. + +`requires_arm`: If set true, can not be loaded on a robot without an arm. + +`no_looping`: If the animation completes before the move's duration, freeze rather than looping. Without this option, the animation might loop because either the move duration was extended or because the `speed` parameter was set to a value greater than 1. + +# Parameters File Section +The parameters section defines adjustable values for the animated move. Each parameter value specified will show in the parameters panel in Choreographer when the animation is selected to be edited/added. The parameter names coincide with the values present in the `AnimateParams` protobuf message (in choreography_params.proto). The animation parameters may only apply for moves which control specific tracks (arm, gripper, body,legs). Each parameter is described on a single line and can be specified two different ways: + +1. The parameter name, followed by the minimum, default, and maximum value (in that order). +``` +PARAMETER_NAME MINIMUM_VALUE DEFAULT_VALUE MAXIMUM_VALUE +``` + +2. Only the parameter name, and the limits/default will be configured from predefined values within Choreographer. +``` +PARAMETER_NAME +``` +Note: If no parameters are needed, put the keywords “no parameters” as the parameter section such that the file will still be parsed correctly. + +## Supported parameters pertaining to all tracks + +`speed`: Play the animation at this time multiplier. + +`offset_slices`: Start the move with the script at this slice. + +## Supported parameters when controlling the body track + +`body_entry_slices`: How many slices to spend transitioning smoothly from the previous pose to the animated pose trajectory for body motion. + +`body_exit_slices`: How many slices to spend transitioning from the animated trajectory back to the nominal pose. If set to 0, will not transition back. + +`translation_multiplier.x`: Multiply the body motion in the x direction. + +`translation_multiplier.y`: Multiply the body motion in the y direction. + +`translation_multiplier.z`: Multiply the body motion in the z direction. + +`rotation_multiplier.roll`: Multiply the body motion in the roll direction. + +`rotation_multiplier.pitch`: Multiply the body motion in the pitch direction. + +`rotation_multiplier.yaw`: Multiply the body motion in the yaw direction. + +`body_tracking_stiffness`: How hard to try to track the animated body motion. Only applicable to animations that control both the body and the legs. On a scale of 1 to 10 (11 for a bit extra). Higher will result in more closely tracking the animated body motion, but possibly at the expense of balance for more difficult animations. + +## Supported parameters when controlling the arm track + +`arm_entry_slices`: How many slices to spend transitioning smoothly from the previous pose to the animated pose trajectory for arm and gripper motion. + +`shoulder_0_offset`: Offset to add to the SH0 angle in all animation keyframes. + +`shoulder_1_offset`: Offset to add to the SH1 angle in all animation keyframes. + +`elbow_0_offset`: Offset to add to the EL0 angle in all animation keyframes. + +`elbow_1_offset`: Offset to add to the EL1 angle in all animation keyframes. + +`wrist_0_offset`: Offset to add to the WR0 angle in all animation keyframes. + +`wrist_1_offset`: Offset to add to the WR1 angle in all animation keyframes. + +`arm_required`: Prevents a robot without an arm from loading the animation. + +`arm_prohibited`: Prevents a robot with an arm from loading the animation. + +## Supported parameters when controlling the gripper track + +`gripper_offset`: Offset to add to the gripper angle in all animation keyframes. + +`gripper_multiplier`: Multiply all gripper angles by this value. + +`gripper_strength_fraction`: How hard the gripper can squeeze. Fraction of full strength. + +## Supported parameters when controlling either (or both) the arm and gripper tracks + +`arm_dance_frame_id`: Dance frame to reference for workspace arm moves. Only valid in combination with the option `arm_playback workspace_dance_frame` (specified in the options section of the file). + +# Body Keyframe File Section + +The body keyframe section defines the actual animated move through keyframes describing either the pose or joint angles at each keyframe timestamp. Each keyframe is a single line consisting of a number of fields determined by the number of columns specified. The first line will specify what value is in each column and how many columns there will be; it is written as a series of key words (space separated) where each keyword has an fixed number of columns that the parser expects. + +Columns defined by keywords can either be an individual definition, or a group definition describing a fixed number of values. For example, “body_pos” describes a group of three columns, and “body_x body_y body_z” describes those same three columns using the individual column keywords. + +The same column value cannot be specified multiple times. Column values are only defined in the first line of the body keyframe section. Some columns are multiple ways of specifying the same body control (for example, “leg_joints” and “foot_pos”) and will be mutually exclusive when defining an animated move. + +Column definitions are required for each track that is being controlled (as specified in the options section). If additional columns controlled unspecified tracks are included, they will be ignored when the animated move is executed. + +## Supported Columns Not Pertaining to a Particular Track + +`time`: The timestamp of each frame. The start of the animation is 0. This column and the “frequency” option are mutually exclusive, but one is required. + +## Supported Columns Pertaining to Gripper Track + +`gripper`: Gripper joint angle. Required. + +The field gripper is required. + +## Supported Columns Pertaining to Arm Track + +`arm_joints`: Grouping of 6 columns [shoulder0 shoulder1 elbow0 elbow1 wrist0 wrist1]. Represents the arm joint angles. Any columns not included will be held constant at the previous joint angle. Mutually exclusive with hand_pos and hand orientation specifications. + +`hand_pos`: Grouping of [hand_x hand_y hand_z]. Hand position in the animation frame. + +`hand_quat_wxyz`: Grouping of [hand_quat_w hand_quat_x hand_quat_y hand_quat_z]. Mutually exclusive with other orientation specifications. + +`hand_quat_xyzw`: Grouping of hand_quat_x hand_quat_y hand_quat_z hand_quat_w]. Mutually exclusive with other orientation specifications. + +`hand_euler_rpy`: Grouping of [hand_roll hand_pitch hand_yaw] Mutually exclusive with other orientation specifications. + +## Supported Columns Pertaining to Body Track + +`body_pos`: Grouping of [body_x body_y body_z]. Body position in the animation frame. Mutually exclusive with com_pos. + +`com_pos`: Grouping of [com_x com_y com_z]. Center of Mass position in the animation frame. Mutually exclusive with body_pos. + +`body_quat_wxyz`: Grouping of [body_quat_w body_quat_x body_quat_y body_quat_z]. Mutually exclusive with other orientation specifications. + +`body_quat_xyzw`: Grouping of [body_quat_x body_quat_y body_quat_z body_quat_w]. Mutually exclusive with other orientation specifications. + +`body_euler_rpy`: Grouping of [body_roll body_pitch body_yaw] Mutually exclusive with other orientation specifications. + +At least one dimension of the body must be specified. + +## Supported Columns Pertaining to Legs Track + +`leg_joints`: Grouping of [fl_hx fl_hy fl_kn fr_hx fr_hy fr_kn hl_hx hl_hy hl_kn hr_hx hr_hy hr_kn]. Can also be grouped by leg as [fl_angles fr_angles hl_angles hr_angles] Mutually exclusive (by leg) with foot_pos. + +`foot_pos`: Grouping of [fl_x fl_y fl_z fr_x fr_y fr_z hl_x hl_y hl_z hr_x hr_y hr_z]. Can also be grouped by leg as [fl_pos fr_pos hl_pos hr_pos]. Mutually exclusive (by leg) with leg_joints. + +`contact`: Grouping of [fl_contact fr_contact hl_contact hr_contact]. 1 if in stance. 0 if in swing. If absent, contact will be inferred from either leg_joints or foot_pos. + +Either leg_joints or foot_pos are required for each leg. diff --git a/docs/concepts/choreography/animations_in_choreographer.md b/docs/concepts/choreography/animations_in_choreographer.md new file mode 100644 index 000000000..4ade4e0ec --- /dev/null +++ b/docs/concepts/choreography/animations_in_choreographer.md @@ -0,0 +1,48 @@ + + + +# Animations in Choreography + +The Choreography service is a framework for producing scripted motion through a list of customizable, predetermined moves. The dances can be customized through the track layering system, the parameters associated with each move, and the adjustable beats per minute of the dances. While these knobs provide a large amount of flexibility when authoring choreographies, there are scenarios where the exact output can't fully be expressed through the existing moves. To do this, we have developed an animation pipeline and API in the 2.4 Spot software release. + +The animation pipeline allows you to create wholly custom sequences using 3D animation tools and integrate them in Choreographer scripts just like the predefined, default moves. For the intro sequence of the [“Spot’s On It” video](https://www.youtube.com/watch?v=7atZfX85nd4), we used Autodesk Maya to produce the kaleidoscoping dance moves. Autodesk Maya is a 3D animation software that gives fine-grain control for authoring and editing kinematics trajectories, but a range of tool sets can be used with this API. + + + +The base representation for the animation is a human-readable text file, so while an animated dance move can be created through 3D animation tools like Autodesk Maya, it can also be hand-written and edited. The primary component of the animated move is a set of timestamped key frames which specify the motion of the different tracks (arms, gripper, legs, or body). The timestamped keyframes that specify the animation can be densely spaced, as they would be from a 3D animation software, or they can be very sparse and the robot will interpolate between each key frame.Additionally, there are components of this text file which enable different animation-specific options and also specifies the different parameters which are associated with this move. A complete overview of the animation file format can be found in the ["Animation File Specification" document](animation_file_specification.md). + +The animated move text files are be parsed into an `Animation` protobuf messages and uploaded to the robot. Once the animation is uploaded to the robot, it can be referenced by name within choreographies. This parsing step (from text file to protobuf message) happens automatically in Choreographer for both animations in the "animations" directory that matches the directory structure shown below, and also for animations uploaded through "File"->"Load Animated Move" menus. + +``` +dance_directory/ + choreographer.exe + animations/ + bourree_arm.cha + my_animation.cha +``` + +There is a python script in the `bosdyn-choreography-client` package, called `animation_file_to_proto.py`, which provides client access to the same functions used by Choreographer to complete the text file to protobuf animation parsing. + +When Choreographer is opened, it begins uploading all animations automatically to every connected robot. Once an animation is uploaded, it will last on the robot until it is rebooted. A dialog will appear indicating the status of all animations being uploaded; the image below shows an example of this dialog. If an animation fails to upload, then check the terminal where the Choreographer executable is running. An error message should be present on the terminal and provide a description of the part of the animation that was invalid. + +![Animation Upload Dialog](images/animation_upload_dialog.png) + +## Choreography Logs for Animations + +To aid in creating animated dance moves without 3D animation software, we have added choreography logs. These logs can be recorded through the choreography log service while driving the robot around with the tablet, moving the robots arm around manually (while it is powered off), or running an existing choreography. The choreography log contains high-rate timestamped key frames with the robot's joint state, foot contacts, and body pose relative to the animation frame (defined by the robot's foot state when the choreography log started). These key frames can be used directly for the animation file's key frames. + +The choreography logs are divided into two types: auto and manual. The log types are used to specify the log's duration and when it is recorded. The "auto" log is recorded whenever a choreography is being executed by the robot. The robot will start recording an "auto" log automatically when a choreography begins, and stop 3 seconds after the completion of a choreography. The "manual" log can be recorded whenever and its duration is defined by when the robot receives a `StartRecordingState` RPC to when it receives a `StopRecordingState` RPC. The manual log has a maximum of 5 minutes of recording. The robot will only keep the most recent "auto" log and the most recent "manual" log saved - therefore the logs must be downloaded immediately to ensure data is not lost. + +In Choreographer, there are buttons to start/stop recording the "manual" log (shown below). As well, there are buttons to download each log type (shown below). The log can be downloaded as a [pickle file](https://docs.python.org/3/library/pickle.html), which tightly packages the data stored as a python dictionary into a serialized object. This is the default format and will be used if no file extension is specified in the file name when saving the log. Additionally, it can be downloaded as a text file, which saves the `ChoreographyStateLog` protobuf message as a protobuf-to-text message; to save in this format, when typing the log name the ".txt" should be explicitly included. + +![Choreographer Log Buttons](images/log_buttons.png) + +The API for the choreography logs is described in the [Choreography Service document](choreography_service.md) and can be accessed via the API in the choreography client. \ No newline at end of file diff --git a/docs/concepts/choreography/choreographer.md b/docs/concepts/choreography/choreographer.md index 393510031..199c127c6 100644 --- a/docs/concepts/choreography/choreographer.md +++ b/docs/concepts/choreography/choreographer.md @@ -12,31 +12,51 @@ Choreographer is a tool for authoring dances and executing them on robots. It le ## Running Choreographer -Choreographer is an executable program we will provide you via a download link at the [Support Center](https://support.bostondynamics.com/). To run the program, simply download it at the link we will provide you, and execute it. Note that the Choreographer executable is only available for Windows. If you wish to run Choreographer from Mac or Linux, we will need to provide you with Python wheels and installation instructions (advanced usage only). Please contact us if that is the case. +Choreographer is an executable program we will provide you via a download link at the [Support Center](https://support.bostondynamics.com/). To run the program, simply download it at the link we will provide you, and execute it. Note that the Choreographer executable is only available for Windows and Linux. If you wish to run Choreographer connected to a Spot, please see the “Connecting Robots to Choreographer” section. ## Choreography Safety -When testing your choreography sequence on a robot, always keep in mind basic safety procedures . Make sure there is plenty of space around your Spot, keep all Spots at least two meters apart from each other, and be sure that neither you nor anyone else approach the dancing Spot. Never approach your Spot unless its motors have been powered off. +When testing your choreography sequence on a robot, always keep in mind basic safety procedures. Make sure there is plenty of space around your Spot, keep all Spots at least two meters apart from each other, and be sure that neither you nor anyone else approach the dancing Spot. Never approach your Spot unless its motors have been powered off. + +### Beginner vs Advanced Mode + +One of the goals for Choreographer is to provide a tool that gives the user as much freedom as they could possibly want. As such, you will be able to find combinations of moves, parameters, and BPM that Spot cannot reliably perform under all conditions. + +Beginner mode provides a more controlled experience that will be more likely to yield reliable results. This mode has smaller parameter ranges which allows for less energetic but generally more reliable dances. Additionally, it has some of the more dynamic dance moves removed from the Moves List and does not provide any support for animated dance moves. + +By default, in the 2.4 released Choreographer executable, it will automatically load in Beginner mode. You can enable it by using the `--restricted` argument when starting Choreographer from the command line. As well, if you are in Advanced mode and go to the Settings menu and select load in Beginner mode, it will take effect and load in this mode next time you open Choreographer. + +You can switch to Advanced mode by selecting the load in Advanced mode checkbox in Choreographer's welcome menu, or navigating to "Settings" and selecting load in Advanced mode (which will take effect next time you open Choreographer). Note that if you create a dance in Advanced mode, Choreographer may not be able to load it while in Beginner mode if the dance’s parameters are outside its reduced range. + +It is highly recommended that Choreographer users start in Beginner mode until they are comfortable using the robot and creating/executing choreographies! ## Interface Overview -![Interface Guide](images/image5.png) + -The Choreographer interface consists of seven main areas. They are: +The Choreographer interface consists of the following important key sections/buttons: -1) **Moves List** - Here you can find all of our predefined moves, sorted by general category. +1) **Moves List** - Here you can find all of our predefined moves (both stock moves and animation moves), sorted by general category, such as "Body" or "Transition". 1) **Dance Timeline** - This is the main area of the Choreographer, and it shows a representation of your dance over time. Each move is a different block, which can be clicked to edit the parameters, dragged around, copy-pasted, or potentially stretched or shrunk if the move parameter's allow it. -1) **Robot Controls** - The robot controls are a row of buttons you can use to send commands to any robots connected to Choreographer, including starting and stopping your dance, and powering on or off your robot’s motors. (Note, this row is disabled when you are not connected to any robots, as shown above.) +1) **Dance Tabs** - Multiple choreography sequences can be opened at once, and will appear as different tabs above the timeline. 1) **Move Name** - When you select a move in the Moves List, its name and description will appear here. 1) **Robot Preview** - This section gives you a preview of the robot’s body and arm during your selected move. Note that this section only appears for select moves that directly position the body of the robot or moves which control the arm. -1) **Move Parameters** - When you select a move that is customizable, its different, adjustable parameters will appear here. You can modify them to adjust how the robot will act during this move. Be sure to test to make sure the robot can handle your parameters! Sometimes more extreme parameters can be too much for the robot during high or low BPM songs, so if a combination of parameters don’t work, adjust them until they do for your situation! Near each parameter's name, a blue question mark may appear which will provide a description of the specific parameter. +1) **Move Parameters** - When you select a move that is customizable, its different, adjustable parameters will appear here. You can modify them to adjust how the robot will act during this move. Be sure to test and make sure the robot can handle your parameters! Sometimes more extreme parameters can be too much for the robot during high or low BPM songs, so if a combination of parameters don’t work, adjust them until they do for your situation! Near each parameter's name, a blue question mark may appear which will provide a description of the specific parameter if you hover the mouse over it. +1) **Robot Controls** - The robot controls are a row of buttons you can use to send commands to any robots connected to Choreographer, including starting and stopping your dance, and powering on or off your robot’s motors. (Note, this row is disabled when you are not connected to any robots, as shown above.) 1) **Music Controls** - This row of controls lets you load and play a song to play during your robot’s dance, manually adjust the BPM of your robot’s moves to match that of your song or the volume of the music, and stop the music and the robot's dancing if one is connected. +1) **Move Configuration/Robot Management Tabs** - Tabs to toggle between the move configuration tab (displays move name and parameters), and the robot management tab (displays all active robot connections and health statistics). +1) **Mode Indicator** - Choreographer has "Beginner" and "Advanced" modes. The title provides an indicator to help remember which mode the application is loaded in. There is an option in the "Settings" menu to switch modes when the application is next re-opened. +1) **Add/Disconnect Buttons** - Buttons which enable dynamically changing which robots are connected and controlled by Choreographer. +1) **Robot Connections** - Each row indicates the robot hostname and other information regarding the robot currently connected to Choreographer. The checkbox indicates whether or not the robot is being controlled (e.g. when unchecked, pressing robot control buttons like "sit" will do nothing to the unselected robot). +1) **Dance Selector** - Drop down menu to choose which of the open choreographies the robot will execute when both checked (in the "Selected" column) and start choreography is clicked. +1) **Health Stats** - Columns which show the power state (on/off), battery state of charge, and any faults for each robot. + ## Choreography File Basics -Spot choreography files consist mainly of a sequence of predefined moves that can be arranged in the timeline of the choreographer (Interface #2). Each move can contain any combination of Gripper, Arm, Body, Legs, or multiple body parts that it affects, and will appear on the appropriate track(s) within the timeline. You can mix and match so that the legs can do a move such as Step while the body does a move such as Rotate Body. However, moves may not overlap on any tracks. +Spot choreography sequence files consist mainly of a sequence of predefined moves that can be arranged in the timeline of Choreographer. Each move can contain any combination of Gripper, Arm, Body, Legs, Lights or multiple body parts that it affects, and will appear on the appropriate track(s) within the timeline. You can mix and match so that the legs can do a move such as Step while the body does a move such as Rotate Body. However, moves may not overlap on any tracks. All choreography files are assumed to be 4/4 signatures in the Choreographer UI. The timeline is broken up into quarter notes (thick vertical lines), each of which is broken up by four lighter vertical lines. Each of those 16th-note intervals is known as a Slice. All moves must be a whole number of slices, and each move must begin and end at a slice boundary. How many slices a move takes is dependent on the BPM (Beats Per Minute) of your song. See Loading Music for more information on how to change your BPM. @@ -44,37 +64,44 @@ All choreography files are assumed to be 4/4 signatures in the Choreographer UI. For example, this "Running Man" move has been extended from the default number of slices, and will now control the legs track for the first 12 slices of this script: -![Slice Diagram](images/image4.png) +![Slice Diagram](images/running_man.png) ## Adding Moves -To add a move to the timeline, there are multiple different methods. You can single click the move in the Moves List, which will open the parameters of the move, but not add it to the timeline. As well, the up and down arrow keys will navigate between different moves in the Moves List once one is selected. While you have a move selected in the Moves List, you can adjust its parameters in the Move Parameters section. - -Once the parameters are adjusted to the desired values, the move can be added to the dance timeline by either 1) pressing the Add button beneath the moves list, 2) double clicking the move name in the Moves List, or 3) entering Insert Mode (described below). If you adjust a move’s parameters and then add it to the timeline, the new Move block that appears in the timeline will have those same modified parameters. +There are multiple different methods to add moves to the timeline. You can single click the move in the Moves List, which will open the name, description, and parameters of the move in the Move Configuration Tab, but will not add it to the timeline. As well, the up and down arrow keys will navigate between different moves in the Moves List once one is selected. While you have a move selected in the Moves List, you can adjust its parameters in the Move Parameters section. -The icon , in the upper left of the Timeline view, will enter Insert Mode when pressed. When in Insert Mode, you can click anywhere in the Timeline to add the selected move (with any of the parameter modifications you have made) to the Timeline at that point. To then exit Insert Mode and re-enter the default Choreographer mode, press the button or hit the escape key on the keyboard. +Once the parameters are adjusted to the desired values, the move can be added to the dance timeline by any of the following methods: +1. Pressing the Add button beneath the moves list, which appends the move to the end of the timeline with any parameter changes. +2. Double clicking the move name in the Moves List, which appends the move to the end of the timeline with any parameter changes. +3. Click the toggle to go from "Append" to "Insert" (under the Moves List). In "insert" mode, hovering the mouse of the timeline will show a "ghost" move block, which when clicked will be added to the timeline with any parameter changes. To exit "insert" mode, hit the escape key on the keyboard or press the toggle again to return to "append" mode. +![Insert/Append Mode Toggle](images/adding_modes.png) +4. Drag a move from the Moves List into the timeline with any parameter changes. Like insert mode, a ghost move block will appear and the mouse can be moved to determine the location to drop and insert the move into the timeline. ## Modifying Move Blocks -Once a move is added to your Timeline, it can be dragged left and right to the appropriate time. Some, but not all, of the moves can be resized by clicking and dragging on the edge of the move’s block. The move will automatically enforce any requirements it has about minimum or maximum duration. Note, to help you with longer moves, the Timeline can be zoomed in/out using the Zoom bar above it. +Once a move is added to your Timeline, it can be dragged left and right to the appropriate time. Some, but not all, of the moves can be resized by clicking and dragging on the edge of the move’s block; hover the mouse over the edge of the move and if it can be resized it will show an arrow instead of the regular mouse. The move will automatically enforce any requirements it has about minimum or maximum duration. Note, to help you with longer moves, the Timeline can be zoomed in/out using the Zoom bar below it. ## Modifying Move Parameters To modify a move’s parameters, simply click it on the timeline to select it, and modify the parameters that appear in the Move Parameters section. Each move has different parameters, and some may not have any parameters at all. Please see the [Moves Reference Guide](move_reference.md) for descriptions of what each parameter does for each move type. Each numerical parameter can be modified by editing its text field, adjusting its slider, or pressing the Up or Down arrow buttons. Boolean parameters can be changed by checking or unchecking the box. Enum parameters are changed by choosing new values in the drop down menu. +A move's parameters can be modified before it is added to the timeline as well. Once the move is selected in the Moves List, the parameters can be edited and when the move is ultimately added to the timeline, it will contain these parameter modifications. + ## Robot Preview The Robot Preview pane will appear for certain applicable moves. You can adjust the camera position and angle of that pane in order to get a better view of your move. Use the scroll wheel to zoom in and out, left click and drag to pan the camera, and right click and drag to rotate it around the preview robot. ## Selecting Multiple Moves -To select multiple moves, click on empty space in the Timeline view, and then drag over the moves that should be selected. To unselect all the moves, click in empty space on the Time view. Note that you cannot edit the parameters of multiple moves at once, but you can drag them around the timeline or copy and paste them all. +To select multiple moves, click on empty space in the Timeline view, and then drag over the moves that should be selected. To unselect all the moves, click in empty space on the Time view. Note that you cannot edit the parameters of multiple moves at once, but you can drag them around the timeline, delete them all, or copy and paste them all. ## Copy / Pasting / Deleting Moves -When you have any number of moves selected, you can Copy (Ctrl+C or Edit->Copy) and Paste (Ctrl+V or Edit->Paste) them as you wish. When you paste moves into your choreography sequence, the new moves will attempt to appear as close as they can to the original move’s location, moving right on the timeline until they can find a place they fit. You can also right click them and choose “Clone” to instantly create a copy of your selected moves, which will also be inserted as close as they can fit into your choreography sequence. +When you have any number of moves selected, you can Copy (Ctrl+C, Edit->Copy) and Paste (Ctrl+V or Edit->Paste) them as you wish. When you paste moves into a choreography sequence, the new moves will attempt to appear as close as they can to the original move’s location, moving right on the timeline until they can find a place they fit. Moves can be copied and pasted between different open dance tabs. + +You can also right click a single move or move grouping and choose “Clone” to instantly create a copy of your selected moves in the currently opened dance, which will also be inserted as close as they can fit into your choreography sequence. -To delete moves, simply select them and either press Delete or Backspace, or choose Edit->Delete in the menus. +To delete moves, simply select them and either press the Delete or Backspace keys, choose Edit->Delete in the top-left menus, or right click and select "Delete". ## Loading Music @@ -84,21 +111,25 @@ Once a dance is loaded, you can preview it by hitting the “Play Music” butto After loading a dance, you must manually set the BPM (Beats Per Minute) of your dance to that of the song. There are many online tools to help you calculate the BPM of any song, but we also provide a metronome to help you with that process if you choose to do it manually. -## Red+Green Sliders +## Red Slider -The red slider allows the user to start the dance at a different location than the beginning; the dance will start at the move associated with the closest slice to the slider's location. The green slider allows the user to adjust when the music starts; the music will begin playing when the dance reaches the slice closest to the slider's location. The picture below circles the two sliders; the lines drawn at the center of the sliders show exactly where in the timeline the slider's location is. They can be moved by clicking on the colored boxes and dragging them to the desired location. +The red slider allows the user to start the dance at a different location than the beginning; the dance will start at the move associated with the closest slice to the slider's location and the music will start at the timestamp within the song associated with the slider's location. The line drawn at the center of the sliders show exactly where in the timeline the slider's location is, as shown in the image below. It can be moved by clicking on the colored boxes and dragging it to the desired location. -![Sliders](images/image6.png) +![Slider](images/red_slider.png) ## Previewing Moves If you have a robot connected to the Choreographer, you can preview moves before adding them to the Timeline. Simply select a move from the Moves List, modify its parameters however you want, and press the Preview Move button. This will cancel all current dances and actions on the robot, and it will perform the one move you have selected. This is a great way to test out parameter modifications before adding it to your move sequence. +![Preview Move Button](images/preview_move_button.png) + ## Performing Choreography Sequences -The “Start Choreography” button in the Robot Controls bar will upload the choreography from the sequence viewer to the robot, and then send a command to execute the routine to the robot while beginning the music at the same starting time specified in the choreography sent to the robot. There will be at least a three second delay (programmed into the button) to ensure that the music and routine can begin at the same time on the robot. Note, if the robot is not started in the proper position (sprawl, sit, stand) and has to automatically make transitions to be ready for the first move in the routine, then the timing of the music starting and the choreography sequence starting will likely be incorrect. +The “Start Choreography” button in the Robot Controls bar will upload the choreography (whichever is selected in the drop down for the robot in the Robot Management Tab - by default, this is the current open choreography tab) to the robot, and then send a command to execute the routine to the robot while beginning the music at the same starting time specified in the choreography sent to the robot. There will be at least a three second delay (programmed into the button) to ensure that the music and routine can begin at the same time on the robot. To adjust this delay, the command line argument `--delay DELAY_IN_SECONDS` can be passed when starting the Choreographer application from the command line. -To stop the choreography routine or the music playing, the “Stop” button will return the robot to a standing position and stop the music. In an emergency, use E-Stop or Power Off instead. +Note, if the robot is not started in the proper position (sprawl, sit, stand) and has to automatically make transitions to be ready for the first move in the routine, then the timing of the music starting and the choreography sequence starting will likely be incorrect. + +To stop the choreography routine (or just stop the music from playing if no robot is connected/executing a choreography), the “Stop” button will freeze the robot with all four feet on the ground and stop the music. In an emergency, use "E-Stop" or "Power Off" buttons instead. ## Saving and Loading Choreography Files @@ -106,73 +137,19 @@ You can save and load Choreographer routines that you create. To save your curre Additionally, you can append an existing choreography sequence at the end of your current dance by going to File->Append Choreography (Ctrl+E), which will automatically add all of the move blocks from that file to the end of your current routine. This is particularly useful if you want to construct a choreography sequence from smaller premade sequences. -## Connecting Robots to Choreographer - -Robots cannot currently be connected to or disconnected from Choreographer while it is running. In order to connect your robot to Choreographer, you must start Choreographer from the command line and pass in the arguments `--hostname {IP/Hostname of your Spot} --user {Username you use to log in to your Spot} --password {Password for your Spot}`. If you wish to connect to multiple Spots at once, simply add more copies of those command line arguments, one set for each Spot. Note that all Spots will do the exact routine, and the Choreographer program does not yet support individual routines for each Spot. In order to accomplish that, you can save your individual routine files and write a custom script to execute them both at the same time on each robot. - -## Robot Controls - -The Robot Controls bar is disabled if there are no robots connected to the Choreographer program. If a robot is connected, the buttons will have the following effects: - - -Button|Function -----|---- -Power Off| Powers off the Spot’s motors. Always press this before approaching your Spot. -Power On | Powers on your Spot’s motors. You must activate this before your Spot can stand or start choreography. -E-Stop | Enables or disables your Spot’s E-Stop. In an emergency, use this to stop the Spot immediately. -Self-Right | If your Spot has fallen, this will attempt to right it into a sitting position -Sit | Sits the Spot in-place. Cancels all current choreography and music, but E-Stop or Power Off should be used in an emergency. -Stand | Brings Spot to a stand. Cancels all current choreography and music, but E-Stop or Power Off should be used in an emergency. -Enable Joystick | Activated Joystick Controls (see Joystick Controls section) -Enable WASD Driving | Activates "WASD" keyboard driving (see WASD Controls section) -Start Choreography | Sends your choreography sequence to your Spot, then starts a 3 second countdown before the robot begins dancing. Any loaded music will automatically start as soon as Spot begins to dance. - -## Joystick Controls - -![](images/image1.png) - -A X-Box gamepad controller can be used with the GUI for convenience of moving and positioning the robot. The button layout is set up for X-Box 360 controllers, which are readily available, and can be connected to a computer through a USB port. Many of the buttons in the GUI are linked to gamepad buttons, and the gamepad button will behave the same as the corresponding GUI button. As shown in the diagram above they are: - -Button | Function -----|----- -A | Stop -B | Enable Joystick -X | Start Choreography -Y | Stand -Left-Bumper | Self Right -Right-Bumper | Sit -Start | Power On -Back | Power Off - -When the joystick is enabled (either through hitting the "B" button on the gamepad or through the GUI button), the robot will walk, and can be driven by the joysticks. As shown in the diagram above, the left joystick controls translation, and the right joystick controls yaw. When the robot is controlled through any of the other Robot Controls buttons, when WASD mode is enabled, or when a dance routine has been started, joystick driving will be disabled however other buttons will still work. - -While using Choreographer, the joystick controller mapping diagram can be accessed as a reminder using the menus Help->Joystick Controller Mapping. ## Keyboard Controls -Similar to the joystick control, we provide the ability to drive the robot using the WASD keys on the keyboard. When enabled (either through hitting the GUI button or by pressing "v" on the keyboard), the robot will walk and can be driven using the WASD keys. Joystick mode will be disabled while driving in WASD mode. The hotkeys are setup to mimic the joystick button key presses when applicable. When the robot is controlled through any of the other Robot Controls buttons, when joystick mode is enabled, or when a dance routine has been started, the WASD driving will be disabled, however other keypresses will still be available. +Choreographer has specific hotkey mappings available to make common actions used when editing choreographies more easily accessible While using Choreographer, a table of available keystrokes can be accessed as a reminder using the menus Help->Hotkeys Documentation. Key | Function ----|----- -v | Enable WASD mode -b | Enable Joystick mode -k | Power On -l | Power Off -y | Stand -x | Start Choreography -[ | Sit -] | Self-right -w | Walk forward -a | Strafe left -s | Walk backwards -d | Strafe right -q | Turn left -e | Turn right - -While using Choreographer, a table of available keystrokes can be accessed as a reminder using the menus Help->Hotkeys Documentation. - -## Restricted Mode - -One of the goals for Choreographer is to provide a tool that gives the user as much freedom as they could possibly want. As such, you will be able to find combinations of moves, parameters, and BPM that Spot cannot reliably perform under all conditions. If, however, you want a more controlled experience that will be more likely to provide more reliable results, we have offered a Restricted Mode for Choreographer. To enable Restricted Mode, simply start Choreographer with an extra `--restricted` argument. Several of the more dynamic moves will be missing, and parameter ranges will generally be smaller, allowing for less energetic and generally more reliable dances. - -Note that if you create a dance in normal mode, Choreographer may not be able to load it while in Restricted Mode if the dance’s parameters are outside its reduced range. +i | Enter "insert" mode +Esc | Exit "insert" mode +p | Play music +Shift + Click | Select multiple moves, adding each one to the selected group when clicked. +Left/Right Arrow Keys | Nudge a move (or group of selected moves) left/right by one slice in the timeline. Cannot cross other moves with nudging; this can only be done when dragging a move (or group). +Shift + Left/Right Arrow Keys | Expand a move on the left/right side by one slice if possible. This only works when a single move is selected (and not a group of moves). +Ctrl + Left/Right Arrow Keys | Shrink a move on the left/right side by one slice if possible. This only works when a single move is selected (and not a group of moves). +Ctrl+C | Copy the move (or group of selected moves). +Ctrl+V | Paste the copied move (or group of selected moves). diff --git a/docs/concepts/choreography/choreographer_setup.md b/docs/concepts/choreography/choreographer_setup.md index 53cf97ed8..17e95020c 100644 --- a/docs/concepts/choreography/choreographer_setup.md +++ b/docs/concepts/choreography/choreographer_setup.md @@ -8,7 +8,7 @@ Development Kit License (20191101-BDSDK-SL). # Install Choreographer -Choreographer is an application to easily author choreographies with advanced moves and parameters and execute the routines on robot with music synchronization. The application and choreography service require a special license to use. The application can be downloaded from the [Support Center](https://support.bostondynamics.com) in the "Downloads Page" for the 2.1 Release. Additionally, the Support Center provides in depth documentation for how to use Choreographer to create routines, modify moves, connect to robots to execute routines, and debug issues with a FAQs section. +Choreographer is an application to easily author choreographies with advanced moves and parameters and execute the routines on robot with music synchronization. The application and choreography service require a special license to use. The application can be downloaded from the [Support Center](https://support.bostondynamics.com) in the "Downloads Page" for the latest Spot software release. Additionally, the Support Center provides in depth documentation for how to use Choreographer to create routines, modify moves, connect to robots to execute routines, and debug issues with a FAQs section. ## System Requirements @@ -23,7 +23,7 @@ The Choreographer application is an executable which can be run directly on a la sudo chmod +x choreographer ``` -To run Choroeographer without any robots connected, just double-click on the executable to open it. +To run Choreographer without any robots connected, just double-click on the executable to open it. To run Choreographer with robots, start Choreographer from the command line, in the directory where the executable was download, with the following options: * On windows: diff --git a/docs/concepts/choreography/choreography_service.md b/docs/concepts/choreography/choreography_service.md index 7a0a825c2..0a9b4452e 100644 --- a/docs/concepts/choreography/choreography_service.md +++ b/docs/concepts/choreography/choreography_service.md @@ -11,14 +11,14 @@ Development Kit License (20191101-BDSDK-SL). ## Overview ### What is it? -The Choreography service is a framework for producing precisely scripted motion, currently focused on dancing. -An example script can be seen on [YouTube](https://www.youtube.com/watch?v=kHBcVlqpvZ8). +The Choreography service is a framework for producing precisely scripted motion, currently focused on dancing. Example choreography scripts can be seen on the Boston Dynamics Youtube channel, showing the robot dancing to [Bruno Mars's "Uptown Funk"](https://www.youtube.com/watch?v=kHBcVlqpvZ8) and [The Contours' "Do You Love Me"](https://www.youtube.com/watch?v=fn3KWM1kuAw). A choreography sequence consists of a series of moves. We can achieve a wide variety of possible behavior from a moderate list of available moves by: 1) Combining multiple moves (see the tracks/layering section). 2) Altering move parameters to vary the behavior of the move. +3) Adjusting the BPM of the choreography sequence to change the speed of the moves. ### Note on Reliability @@ -38,24 +38,30 @@ Note that any number of slices per minute can be selected, however very fast or ### Tracks/Layering -We divide the robot’s motion into four distinct tracks: +We divide the robot’s motion into the following distinct tracks: * Legs * Body * Arm * Gripper -Each dance move requires one or more of these tracks. Moves that use different tracks can be run simultaneously in any combination. In Choreographer, a track is represented as a horizontal section in the timeline view. For example, here is a screenshot from Choreographer of a script that combines moves in all three of the four tracks: +In addition to the base motion, there are also tracks for: -![](images/main_image1.png) +* Lights: controls the robot's front two sets of LEDs. +* Annotations: enables dance annotations that are separate from any specific move. +* (Choreographer Only) Music: controls the audio played from the Choreographer application when dancing. + +Each dance move requires one or more of these tracks. Moves that use different tracks can be run simultaneously in any combination. In Choreographer, a track is represented as a horizontal section in the timeline view. For example, here is an image from Choreographer of a script that combines moves in three of the four tracks: + +![Tracks](images/tracks_labeled.png) And the resulting behavior looks like this: -![](gif_images/main_image3.gif) +![Dancing Behavior Gif](gif_images/main_image3.gif) -Some moves require multiple tracks, such as the "Skip" move which uses the Body and Legs track or the "Arm Move Relative" which uses the Arm and Gripper tracks, as shown in this example: +Some moves require multiple tracks, such as the "Jump" move which uses the Body and Legs track or the "Arm Move" which uses the Arm and Gripper tracks, as shown in this example: -![](images/main_image4.png) +![](images/multi_tracked_dance.png) ### Entry/Exit conditions @@ -74,10 +80,12 @@ The first leg-track move can have any entry state, and the robot will automatica All subsequent legs-track moves must have an entry state that corresponds to the previous legs-track move’s exit state. Scripts that violate this requirement will be rejected by the API and return warnings indicating which moves violate the entry/exit states. As well, routines made in Choreographer will highlight moves red when the entry state does not match the previous leg move's exit state, such as in this example: -![](images/main_image2.png) +![Transitions Error](images/transition_error.png) ## API +### Choreography API Interface + The API defines a choreography sequence by a unique name, the number of slices per minute, and a repeated list of moves. Each move consists of the move’s type, its starting slice, duration (in slices), and the actual parameters (`MoveParams` proto message). The `MoveParams` message describe how the robot should behave during each move. For example, a move parameter could specify positions for the body. Each parameter may have specific limits/bounds that are described by the `MoveInfo` proto; this information can be found using the `ListAllMoves` RPC. Once a choreography sequence is created, the `UploadChoreography` RPC will send the routine to the robot. The choreography service will validate and check the structure of the routine to ensure it is feasible and within bounds. @@ -86,8 +94,34 @@ The service will return a list of warnings and failures related to the uploaded The `ExecuteChoreography` RPC will run the choreography sequence to completion on the robot. A choreography sequence is identified by the unique name of the sequence that was uploaded to the robot. Additionally, a starting time (in robot’s time) and a starting slice will fully specify to the robot when to start the choreography sequence and at which move. +### Animation API Interface + +The API defines an animated move (`Animation` proto message) by a unique name, a repeated list of `AnimationKeyFrames` which describes the robot's motion at each timestamp, and additional parameters and options which fully describe how the move should be executed. Unlike other dance moves, the information about the minimum and maximum parameters, if the move is extendable, or which tracks the move controls are not known to the robot beforehand and must be specified in the `Animation` protobuf. + +The `Animation` can be uploaded to the robot using the `UploadAnimatedMove` RPC, which will send the animation to the robot. The choreography service will validate and check the structure of the animation to ensure it is fully specified and is feasible. If the animation does not pass this validation, the RPC will respond with a failure status and a set of warning messages indicating which parts of the animation failed. + +If the animation uploads successfully, then it can be used within choreography sequences and will appear as a move option in Choreographer with any of the parameters that were specified in the initial `Animation` protobuf message. The move type associated with the uploaded animation is "animation::" + the animation's name. The animations will persist on robot until either the robot is powered off or an animation with the exact same name is uploaded (overwriting the previous animation with that name). + +While animations can be written manually using protobuf in any application, we have also provided a way to create animations from human-readable text files. The animation text file has the extension *.cha and has a specific format which is described in the [animation file specification document](animation_file_specification.md). The animation *.cha file can be converted into an `Animation` protobuf message using the `animation_file_to_proto.py` script provided in the choreography client library. + +### Choreography Logs API Interface + +The API defines a choreography log using the `ChoreographyStateLog` protobuf message, which consists of a repeated series of timestamped key frames that contain the joint state of the entire robot, the foot contacts, and the SE3Pose for the robot body relative to the animation frame. The animation frame is defined based on the feet position at the beginning of the animation: the position is the center of all four feet, and the rotation is yaw only as computed from the feet positions. + +The choreography logs are divided into two types: +- Automatic/"Last Choreography" Logs: This log is recorded from when the `ExecuteChoreography` RPC is first received to 3 seconds after the completion of the choreography. +- Manual Logs: This log is recorded from when a `StartRecordingState` RPC is received to when a `StopRecordingState` RPC is received. There is a maximum of 5 minutes of recording for a manual log. + +The choreography logs can be used to review how the robot actually executed a choreography or animated dance move. For example, specifically for animations, if the move is not completely feasible, the robot will attempt to get as close to what was asked as possible but may not succeed. The choreography log can be used to understand and update the animated move to be realistic. + +The `DownloadRobotStateLog` RPC can be used to download the choreography logs. The request specifies which type of log should be downloaded. The response is streamed over grpc and recombined by the choreography client to create a full log message. The robot only keeps one auto log and one manual log in its buffer (2 total logs) at a time, so the log must be downloaded immediately after completing the move on robot. + +### Choreography Client + The choreography service has a python client library which provides helper functions for each RPC as well as functions that help convert the choreography sequence from a protobuf message into either a binary or text file. +### Python Examples using the Choreography API + The [upload_choreographed_sequence example](../../../python/examples/upload_choreographed_sequence/README.md) demonstrates how to read an existing routine from a saved text file, upload it to the robot, and then execute the uploaded choreography. ## Config Files @@ -110,6 +144,8 @@ The `MoveInfoConfig` can be parsed by a protobuf parser into each moves field of * controls_legs: Does this move require the legs track. * controls_body: Does this move require the body track. * controls_gripper: Does this move require the gripper track. +* controls_lights: Does this move require the front LED lights. +* controls_annotations: Does this move update the overall dance state. * display: Information for how Choreographer should display the move. * color: The color to draw the box for the move in the timeline tracks. * markers: The slices to draw the small grey vertical lines. These usually correspond to events such as touchdown and liftoff, and help the user line those events up as desired (e.g. on the beat). Negative values here indicate slices before the end of the move. diff --git a/docs/concepts/choreography/gif_images/Goto.gif b/docs/concepts/choreography/gif_images/Goto.gif new file mode 100644 index 000000000..e4d21b733 Binary files /dev/null and b/docs/concepts/choreography/gif_images/Goto.gif differ diff --git a/docs/concepts/choreography/images/adding_modes.png b/docs/concepts/choreography/images/adding_modes.png new file mode 100644 index 000000000..86eca4bd4 Binary files /dev/null and b/docs/concepts/choreography/images/adding_modes.png differ diff --git a/docs/concepts/choreography/images/animation_upload_dialog.png b/docs/concepts/choreography/images/animation_upload_dialog.png new file mode 100644 index 000000000..a85bf7f43 Binary files /dev/null and b/docs/concepts/choreography/images/animation_upload_dialog.png differ diff --git a/docs/concepts/choreography/images/choreography_interface.png b/docs/concepts/choreography/images/choreography_interface.png new file mode 100644 index 000000000..632ceda40 Binary files /dev/null and b/docs/concepts/choreography/images/choreography_interface.png differ diff --git a/docs/concepts/choreography/images/choreography_interface_robot_management.png b/docs/concepts/choreography/images/choreography_interface_robot_management.png new file mode 100644 index 000000000..69d223457 Binary files /dev/null and b/docs/concepts/choreography/images/choreography_interface_robot_management.png differ diff --git a/docs/concepts/choreography/images/image2.png b/docs/concepts/choreography/images/image2.png deleted file mode 100644 index 56d1cd8d7..000000000 Binary files a/docs/concepts/choreography/images/image2.png and /dev/null differ diff --git a/docs/concepts/choreography/images/image3.png b/docs/concepts/choreography/images/image3.png deleted file mode 100644 index ba52ea61f..000000000 Binary files a/docs/concepts/choreography/images/image3.png and /dev/null differ diff --git a/docs/concepts/choreography/images/image4.png b/docs/concepts/choreography/images/image4.png deleted file mode 100644 index fbfe8ac13..000000000 Binary files a/docs/concepts/choreography/images/image4.png and /dev/null differ diff --git a/docs/concepts/choreography/images/image5.png b/docs/concepts/choreography/images/image5.png deleted file mode 100644 index 8045446de..000000000 Binary files a/docs/concepts/choreography/images/image5.png and /dev/null differ diff --git a/docs/concepts/choreography/images/image6.png b/docs/concepts/choreography/images/image6.png deleted file mode 100644 index c6315014a..000000000 Binary files a/docs/concepts/choreography/images/image6.png and /dev/null differ diff --git a/docs/concepts/choreography/images/image1.png b/docs/concepts/choreography/images/joystick_help.png similarity index 100% rename from docs/concepts/choreography/images/image1.png rename to docs/concepts/choreography/images/joystick_help.png diff --git a/docs/concepts/choreography/images/log_buttons.png b/docs/concepts/choreography/images/log_buttons.png new file mode 100644 index 000000000..e5bbcdc00 Binary files /dev/null and b/docs/concepts/choreography/images/log_buttons.png differ diff --git a/docs/concepts/choreography/images/main_image2.png b/docs/concepts/choreography/images/main_image2.png deleted file mode 100644 index c1b58c26c..000000000 Binary files a/docs/concepts/choreography/images/main_image2.png and /dev/null differ diff --git a/docs/concepts/choreography/images/main_image4.png b/docs/concepts/choreography/images/main_image4.png deleted file mode 100644 index d7ab0343e..000000000 Binary files a/docs/concepts/choreography/images/main_image4.png and /dev/null differ diff --git a/docs/concepts/choreography/images/multi_tracked_dance.png b/docs/concepts/choreography/images/multi_tracked_dance.png new file mode 100644 index 000000000..4bfd6ea99 Binary files /dev/null and b/docs/concepts/choreography/images/multi_tracked_dance.png differ diff --git a/docs/concepts/choreography/images/preview_move_button.png b/docs/concepts/choreography/images/preview_move_button.png new file mode 100644 index 000000000..95222fd22 Binary files /dev/null and b/docs/concepts/choreography/images/preview_move_button.png differ diff --git a/docs/concepts/choreography/images/red_slider.png b/docs/concepts/choreography/images/red_slider.png new file mode 100644 index 000000000..581f7edc4 Binary files /dev/null and b/docs/concepts/choreography/images/red_slider.png differ diff --git a/docs/concepts/choreography/images/robot_management.png b/docs/concepts/choreography/images/robot_management.png new file mode 100644 index 000000000..be5bac5b5 Binary files /dev/null and b/docs/concepts/choreography/images/robot_management.png differ diff --git a/docs/concepts/choreography/images/running_man.png b/docs/concepts/choreography/images/running_man.png new file mode 100644 index 000000000..cf2409041 Binary files /dev/null and b/docs/concepts/choreography/images/running_man.png differ diff --git a/docs/concepts/choreography/images/main_image1.png b/docs/concepts/choreography/images/tracks_labeled.png similarity index 100% rename from docs/concepts/choreography/images/main_image1.png rename to docs/concepts/choreography/images/tracks_labeled.png diff --git a/docs/concepts/choreography/images/transition_error.png b/docs/concepts/choreography/images/transition_error.png new file mode 100644 index 000000000..4d6e4930d Binary files /dev/null and b/docs/concepts/choreography/images/transition_error.png differ diff --git a/docs/concepts/choreography/move_reference.md b/docs/concepts/choreography/move_reference.md index 96bfdaab3..118407236 100644 --- a/docs/concepts/choreography/move_reference.md +++ b/docs/concepts/choreography/move_reference.md @@ -102,6 +102,26 @@ pivot | What part of the robot to pivot around. Pivoting around the front means clockwise | Which direction to rotate. starting_angle | Where in the circle to start rotation. Since it spirals outwards, it may not be obvious exactly where it starts. +### fidget_stand + +A procedurally-generated idle animation. It looks around, breathes, shifts its weight, and stamps. Can use one of the preset configurations or customize the parameters. + +Parameter | Effect +--|-- +preset | Pre-designed parameter combinations that attempt to convey a specific emotion. NOTE: All other sliders are only active if this is set to "Custom". +min_gaze_pitch | How far down it will look. (Radians) +max_gaze_pitch | How far up it will look. (Radians) +gaze_mean_period | How frequently it will look somewhere else. (Seconds) +gaze_center_cfp | Where the gaze array originates in the center-of-footprint frame. (Meters) +shift_mean_period | How frequently it will shift its weight. (Seconds) +shift_max_transition_time | Maximum amount of time it will spend shifting its weight. (Seconds) +breath_min_z | Minimum amplitude of the "breathing". (Meters) +breath_max_z | Maximum amplitude of the "breathing". (Meters) +leg_gesture_mean_period | How frequently it will do a leg gesture. (Seconds) +gaze_slew_rate | How quickly it will shift its gaze. (Meters/Second) +gaze_position_generation_gain | How much Brownian motion will be applied to the gaze point. +gaze_roll_generation_gain | How much Brownian motion will be applied to the gaze roll. + ## Step Moves ### step @@ -123,6 +143,20 @@ swing_height | How high to lift the foot/feet. Does nothing if a swing_waypoint liftoff_velocity | How quickly to raise the foot/feet. Does nothing if a swing_waypoint is specified. touchdown_velocity | How quickly to lower the foot/feet. Does nothing if a swing_waypoint is specified. +### goto + +![](gif_images/Goto.gif) + +Trot to a specified position in the dance frame. (Unless explicitly set, the dance frame is defined by where the dance began.) Takes 1 step per beat. Extend the duration to successfully move farther. + +Parameter | Effect +--|-- +absolute_position | Position we should go to in the dance frame. +absolute_yaw | Yaw orientation we should go to in the dance frame. +step_position_stiffness | How precisely should we step in the nominal locations. +duty_cycle | What fraction of the time a foot is on the ground. 0.5 is a standard trot. +link_to_next | Should we smoothly transition from this move to a subsequent goto move. + ### trot ![](gif_images/trot.gif) @@ -153,8 +187,12 @@ Take two steps to turn in place. Requires 2 beats (8 slices). Parameter | Effect --|-- -yaw | How far to turn. -absolute | If true, yaw is interpreted relative to the orientation the choreography sequence began in. If false, yaw is interpreted relative to the orientation before entering the move. +motion_is_absolute | Is motion in the dance frame (true) or relative to the current position (false). +motion | How far to move. (Meters) +absolute_motion | Where to move to in the dance frame. (Meters) +yaw_is_absolute | Is yaw in the dance frame (true) or relative to the current position (false). +yaw | How far to rotate. (Radians) +absolute_yaw | Where to rotate to in the dance frame. (Radians) swing_height | How how to pick up the feet. Zero indicates to use a default swing. swing_velocity | How quickly to lift off and touch down the feet | Zero indicates to use a default swing. @@ -166,10 +204,14 @@ Take two steps to translate using a pace gait (legs on the same side of the robo Parameter | Effect --|-- -motion | How far and what direction to move. +motion_is_absolute | Is motion in the dance frame (true) or relative to the current position (false). +motion | How far to move. (Meters) +absolute_motion | Where to move to in the dance frame. (Meters) +yaw_is_absolute | Is yaw in the dance frame (true) or relative to the current position (false). +yaw | How far to rotate. (Radians) +absolute_yaw | Where to rotate to in the dance frame. (Radians) swing_height | How how to pick up the feet. Zero indicates to use a default swing. swing_velocity | How quickly to lift off and touch down the feet | Zero indicates to use a default swing. -absolute | Motion is measured relative to the dance’s starting location rather than the current location. May not be accurate for longer dances with lots of stepping. ### crawl @@ -266,13 +308,17 @@ Jumps in place. Nominally lasts one beat, but may take more slices at faster te Parameter | Effect --|-- -yaw | What orientation to land in. -absolute | If true, yaw is interpreted relative to the orientation the choreography sequence began in. If false, yaw is interpreted relative to the orientation before entering the move. +translation_is_absolute | Is motion in the dance frame (true) or relative to the current position (false). +translation | How far to move. (Meters) +absolute_translation | Where to move to in the dance frame. (Meters) +yaw_is_absolute | Is yaw in the dance frame (true) or relative to the current position (false). +yaw | How far to rotate. (Radians) +absolute_yaw | Where to rotate to in the dance frame. (Radians) flight_slices | How long the jump should last with all feet off the ground measured in slices. Depending on tempo, higher values will be unreliable. stance_width/stance_length | The footprint the robot should land its jump in. +swing_height | How how to pick up the feet. split_fraction | Splits the liftoff and touchdown into two pairs of two legs. In fraction of of swing with two legs in flight, so 0 means all legs fully synchronized. lead_leg_pair | If split_fraction is not 0, indicates which legs lift off first. Default AUTO mode will select a pair based on the translation vector. -translation | Distance and direction the robot should jump. ## Transition Moves @@ -486,6 +532,17 @@ Parameter | Effect angle | The desired gripper angle. speed | The desired velocity of the gripper in rad/s. +### frame_snapshot + +This move does not directly do anything. Instead it sets the frame to be used by future moves that are in an absolute frame. Some such moves will always be in the dance frame (frame_id = 0), and some allow you to explicitly select a frame_id. This move can set frames relative to either a fiducial or to the robot's footprint. + +Parameter | Effect +--|-- +frame_id | Which frame to set. 0 is the dance frame and will be used by moves that do absolute motion but don't explicitly select a frame. +fiducial_number | Which fiducial to set the frame relative to. If set to -1 or the fiducial has not been seen recently, will set according to the robot's footprint. +include_each_leg | Should each leg be included when determining the footprint. +compensated | If a foot is not included, should we offset the footprint as if that foot were in a nominal stance? Otherwise, we'll take the center of the included feet. + ### chicken_head ![](gif_images/chickenhead-opt.gif) @@ -498,3 +555,58 @@ bob_magnitude | A vector describing the amplitude and direction of a periodic mo beats_per_cycle | How long it takes to complete 1 cycle of the motion described by bob_magnitude follow | If set to true, the gripper position will adjust if the robot steps to a new location. +## Lights moves + +All colors are specified as RGB on a 0-255 scale. + +### set_color + +Sets the color of the robot's face lights. Can independently set left and right lights if desired. Can fade in and out gradually if desired. + +Parameter | Effect +--|-- +left_color | Color of the left lights. (Color of all lights if right_same_as_left specified.) +right_same_as_left | If true, all lights are left_color. If false, left and right are independently specified. +right_color | Color of the right lights. Does nothing if right_same_as_left specified. +fade_in_slices | How long to spend brightening at the beginning, measured in slices (1/4 beats). +fade_out_slices | How long to spend darkening at the end, measured in slices (1/4 beats). + +### fade_color + +Sets the lights to show one color at the top fading towards another color at the bottom. + +Parameter | Effect +--|-- +top_color | The color of the topmost lights. +bottom_color | The color of the bottommost lights. +fade_in_slices | How long to spend brightening at the beginning, measured in slices (1/4 beats). +fade_out_slices | How long to spend darkening at the end, measured in slices (1/4 beats). + +### independent_Color + +Independently specifies the color for all 8 of the lights. + +Parameter | Effect +--|-- +top_left | Color of the top left light. +upper_mid_left | Color of the second from top left light. +lower_mid_left | Color of the second from bottom left light. +bottom_left | Color of the bottom left light. +top_right | Color of the top right light. +upper_mid_right | Color of the second from top right light. +lower_mid_right | CColor of the second from bottom right light. +bottom_right | Color of the bottom right light. +fade_in_slices | How long to spend brightening at the beginning, measured in slices (1/4 beats). +fade_out_slices | How long to spend darkening at the end, measured in slices (1/4 beats). + +### ripple_Color + +Sets the lights in a variety of moving patterns. + +Parameter | Effect +--|-- +main | Color to make the lights. +secondary | Second color to make the lights, used only by some patterns. +pattern | Select from a variety of light motion patterns. +light_side | Which side lights to use for the pattern. +increment_slices | How quickly to move the pattern. Gives the time in slices (1/4 beats) between updates to the pattern. \ No newline at end of file diff --git a/docs/concepts/choreography/robot_controls_in_choreographer.md b/docs/concepts/choreography/robot_controls_in_choreographer.md new file mode 100644 index 000000000..243998a5f --- /dev/null +++ b/docs/concepts/choreography/robot_controls_in_choreographer.md @@ -0,0 +1,85 @@ + + +# Connecting Robots to Choreographer + +Robots can either be connected to Choreographer before starting the application, or they can be dynamically connected and disconnected from the application once it is running. + +In order to connect your robot to Choreographer, you must start Choreographer from the command line and pass in the arguments `--hostname {IP/Hostname of your Spot} --user {Username you use to log in to your Spot} --password {Password for your Spot}`. If you wish to connect to multiple Spots at once, simply add more copies of those command line arguments, one set for each Spot. + +To connect your robot after the application has been started, select the Robot Management Tab (shown in the image below), and then click the "Add" button. This will prompt you for the robot's hostname, username, and then password. To remove any robot connections, click the "Disconnect" button and check any robots that you want to disconnect from. Note: disconnecting from a robot will power off the robot and release the lease and E-Stop. + +![Robot Management Tab](images/robot_management.png) + +Choreographer supports connecting to multiple robots at once. In the Robot Management Tab, you can select which robots are being controlled at a given time. Additionally, you can apply different choreographies to each robot and start them all at the same time. + +## Robot Controls + +The Robot Controls bar is disabled if there are no robots connected to the Choreographer program. If a robot is connected, the buttons will have the following effects: + + +Button|Function +----|---- +Power Off| Powers off the Spot’s motors. Always press this before approaching your Spot. +Power On | Powers on your Spot’s motors. You must activate this before your Spot can stand or start choreography. +E-Stop | Enables or disables your Spot’s E-Stop. In an emergency, use this to stop the Spot immediately. +Self-Right | If your Spot has fallen, this will attempt to right it into a sitting position +Sit | Sits the Spot in-place. Cancels all current choreography and music, but E-Stop or Power Off should be used in an emergency. +Stand | Brings Spot to a stand. Cancels all current choreography and music, but E-Stop or Power Off should be used in an emergency. +Enable Joystick | Activated Joystick Controls (see Joystick Controls section) +Enable WASD Driving | Activates "WASD" keyboard driving (see WASD Controls section) +Return To Start | Spot will walk from its current location to the last location it started a dance (either a full choreography or the move preview dance). +Start Choreography | Sends your choreography sequence to your Spot, then starts a 3 second countdown before the robot begins dancing. Any loaded music will automatically start as soon as Spot begins to dance. + +Note: The return to start button (added in the Spot 2.4 release) will bring all selected robots back to their starting position after completing a choreography. If multiple robots are being controlled, there is obstacle avoidance enabled when they are navigating back to the starting position. To adjust this obstacle padding distance (in meters), the command line argument `--obs-padding DIST_IN_METERS` can be provided when starting the Choreographer application. + +## Joystick Controls + +![Joystick Controls](images/joystick_help.png) + +A X-Box gamepad controller can be used with the GUI for convenience of moving and positioning the robot. The button layout is set up for X-Box 360 controllers, which are readily available, and can be connected to a computer through a USB port. Many of the buttons in the GUI are linked to gamepad buttons, and the gamepad button will behave the same as the corresponding GUI button. As shown in the diagram above they are: + +Button | Function +----|----- +A | Stop +B | Enable Joystick +X | Start Choreography +Y | Stand +Left-Bumper | Self Right +Right-Bumper | Sit +Start | Power On +Back | Power Off + +When the joystick is enabled (either through hitting the "B" button on the gamepad or through the GUI button), the robot will walk, and can be driven by the joysticks. As shown in the diagram above, the left joystick controls translation, and the right joystick controls yaw. When the robot is controlled through any of the other Robot Controls buttons, when WASD mode is enabled, or when a dance routine has been started, joystick driving will be disabled however other buttons will still work. + +While using Choreographer, the joystick controller mapping diagram can be accessed as a reminder using the menus Help->Joystick Controller Mapping. + +Note: the X-Box controller must be connected to the computer via USB port before opening the Choreographer application. + +## Keyboard Controls + +Similar to the joystick control, we provide the ability to drive the robot using the WASD keys on the keyboard. When enabled (either through hitting the GUI button or by pressing "v" on the keyboard), the robot will walk and can be driven using the WASD keys. Joystick mode will be disabled while driving in WASD mode. The hotkeys are setup to mimic the joystick button key presses when applicable. When the robot is controlled through any of the other Robot Controls buttons, when joystick mode is enabled, or when a dance routine has been started, the WASD driving will be disabled, however other keypresses will still be available. + +Key | Function +----|----- +v | Enable WASD mode +b | Enable Joystick mode +k | Power On +l | Power Off +y | Stand +x | Start Choreography +[ | Sit +] | Self-right +w | Walk forward +a | Strafe left +s | Walk backwards +d | Strafe right +q | Turn left +e | Turn right + +While using Choreographer, a table of available keystrokes can be accessed as a reminder using the menus Help->Hotkeys Documentation. diff --git a/docs/concepts/developing_api_services.md b/docs/concepts/developing_api_services.md index b389cd766..5102a1623 100644 --- a/docs/concepts/developing_api_services.md +++ b/docs/concepts/developing_api_services.md @@ -40,7 +40,7 @@ service_runner = GrpcServiceRunner(service_servicer, add_servicer_to_server_fn, # Run the server until a SIGINT is received. service_runner.run_until_interrupt() ``` -The `image_service_pb2_grpc.add_ImageServiceServicer_to_server` is a function auto generated from a protobuf service definition. The function links a servicer to a server and is generated for every protobuf server compiled. The `WebCamImageServicer` is a custom class that inherits from the servicer class auto generated from the protobuf service definition. It defines methods for responding to all possible service requests. The [`GrpcServiceRunner`](../../python/bosdyn-client/src/bosdyn/client/util.py) class will create and run a server object associated with the passed in servicer. It will monitor for requests at the given port. The `run_until_interrupt()` method can be used to keep a server alive until it receives a SIGINT. +The `image_service_pb2_grpc.add_ImageServiceServicer_to_server` is a function auto generated from a protobuf service definition. The function links a servicer to a server and is generated for every protobuf server compiled. The `WebCamImageServicer` is a custom class that inherits from the servicer class auto generated from the protobuf service definition. It defines methods for responding to all possible service requests. The [`GrpcServiceRunner`](../../python/bosdyn-client/src/bosdyn/client/server_util.py) class will create and run a server object associated with the passed in servicer. It will monitor for requests at the given port. The `run_until_interrupt()` method can be used to keep a server alive until it receives a SIGINT. #### Registering a Service Registering a service requires communication with the robot. A service can register itself via the Directory Registration Client provided with the Python SDK. Each service instance should have a unique service name, service authority, and service type associated with it that are provided at registration time. These details will enable Spot to route client requests to the proper service. The preferred method of registering a service is shown below. Note that it registers with a directory keep alive and will have liveness monitoring enabled. diff --git a/docs/concepts/lease_example.png b/docs/concepts/lease_example.png new file mode 100644 index 000000000..77fa1db08 Binary files /dev/null and b/docs/concepts/lease_example.png differ diff --git a/docs/concepts/lease_service.md b/docs/concepts/lease_service.md new file mode 100644 index 000000000..1385876eb --- /dev/null +++ b/docs/concepts/lease_service.md @@ -0,0 +1,69 @@ + + +# Lease Service + +Leases represent ownership of the robot for the purpose of issuing commands to control the robot. There can only ever be a single owner of a specific robot resource at any given time, however an application can delegate the lease to other systems to perform various tasks. Leases can be returned by one application and subsequently acquired by a different application to change ownership. Alternatively, a lease can be revoked by another application to change ownership forcefully. + +In addition to identifying ownership, the lease system is used to confirm that a robot is still being controlled and that there are valid communications to the owner. If an owner can no longer be reached within a certain period of time, the lease will automatically be revoked from that owner. + +## Basic Lease Usage + +Services which command or control the robot require a lease. These services will reject incoming requests which are from non-owners or contain older ownership information. Ownership semantics provides additional safety to operation workflows by preventing accidental commands from disturbing the current owner’s application. Note, some services will only read information from the robot, and do not require a lease (e.g. image service or robot-state service). + +Applications which want to control the robot will begin by acquiring a lease to the robot. A lease can be acquired with the `AcquireLease` RPC when it has no existing owner. + +Throughout the duration of the application, the client should continue to send “keep-alive” signals to continue ownership of the robot’s resources. The “keep-alive” signal is sent as the `RetainLease` RPC. If enough time passes between the last retain lease request received, the robot will revoke ownership of the lease such that other applications can acquire ownership. + +When issuing robot commands, the robot will be sent a version of the lease with the specific command to permit the robot to complete the desired command. This lease will automatically be included with the command request when using the SDK + +Finally, when the application is complete, it should return the lease. The `ReturnLease` RPC should be used when a client is finished with their ownership to indicate that the robot is no longer owned without waiting for a revocation timeout. Note, applications should only return the lease when it was the one who acquired the lease using the `AcquireLease` RPC. In the case of a remote mission service callback or other services which are delegated the lease by a different main service, the application's service should *not* return the lease when finished - the main service acquired the lease and will return it when complete. + +## [Advanced] Lease Resources + +A lease can claim ownership of the entire robot or specific resources. The “body” resource describes ownership of the entire robot, and can be broken down into sub-resources for the arm, gripper, and mobility (controlling only the legs), as shown in the image below. In most use cases, an application simply needs to send the "body" lease and the robot will determine which specific resources it needs for the command being issued. + +![Resource Tree](resource_tree.png) + +The different robot resources create a tree-structured hierarchy such that the client can control a subset of the robot or the entire robot without needing to manage individual resources. Owning a lease with a parent resource represents control over the entire subtree. + +## [Advanced] Additional Lease Usage Patterns + +Typically, the lease and ownership can be gained using the `AcquireLease` RPC> If the lease is actively being owned, the `TakeLease` RPC provides an alternative that will forcefully take ownership of the lease resources to give control to the application issuing the RPC. This should only be used expressly by a human who is aware of what they’re taking control from. + +When issuing a specific command which requires a lease, the robot should be sent a sub-lease of the main, acquired lease. This delegates the ownership of that lease’s resources to the particular service being sent the command. Note, the creation of a sub-lease for a specific command is done automatically by the client library. Commands which require a lease will respond with `LeaseUseResult` protos, which reflect whether or not the lease provides proper ownership of the robot. + +Additionally, clients should simply send the “body” lease and the robot service’s will split the lease into only the necessary parts. This allows clients to issue multiple commands which control different resources (e.x. a gripper command and a body trajectory command) without worrying about breaking apart a lease themselves. Clients can split leases into sub-resources, but will need to be careful to preserve the sequence number. + +## [Advanced] Lease Representation + +A lease contains the resource which it controls ownership, a sequence, an epoch, and set of client names. + +The sequence is a list of integer numbers which indicates when the lease was generated and if it is a sub-lease. The first number in the sequence is the root lease number, and is generated by the base lease service. Sequence numbers are incremented when creating new leases. Additional sequence numbers can be appended to the lease sequence when creating a sub-lease to delegate ownership to a specific service. For example, the lease sequence `lease=[3, 1]` has a root lease number of `3`, and a single sub-lease with value `1`. + +Resource ownership is determined by whichever lease has the highest root lease number. For example, when comparing `leaseA = [6, 1]` and `leaseB = [5, 13]`, leaseA is considered newer and the current owner. + +In addition to ownership, the lease sequences are used by services to determine which command should be executing currently. Services will use and execute whichever command shows ownership of the necessary resources and has the newest lease sequence. To compare leases, the sequence number with the first higher number is considered the newest lease. For example, when comparing `leaseA = [1, 2, 10]` and `leaseB = [1, 2, 11]`, `leaseB` is considered newer since the second sequence number is higher in `leaseB` than in `leaseA`. + +The epoch is used to create a scope for the sequence field, such that only leases with the same epoch can be compared. The client names are a list of each client which has held the lease; these can be helpful for debugging to see which services were delegated ownership and created sub-leases. + +Consider the example below. + +![Lease Usage Example](lease_example.png) + +A client application took the lease from the tablet (or the tablet lost communication) and now owns the robot. The client application delegates control to graph-nav by creating a sub-lease, and graph-nav again creates a sub-lease to give control to the robot command service. When the tablet attempts to issue a robot command, it will be rejected because it is now considered older. + +## [Advanced] Understanding Lease Errors + +When a command is sent including a lease, the service checks the following things before accepting the command: + - The incoming lease is valid: contains the current epoch, has known robot resources and the resources are correct for this type of command, has a non-empty sequence with a root lease number which has been issued by the lease service. + - The incoming lease shows ownership as the newest lease: compares the incoming lease with the service’s current known lease. + +The service will then respond to the command with a `LeaseUseResult` proto that indicates whether the incoming lease passed the checks and includes additional information to aid in debugging. + +When a command with a lease fails with a lease error, the status within the lease use result provides the root-cause of the failure. In addition to the status, the LeaseUseResult proto contains information about the current lease owner of the incoming lease’s resource, the most recent lease known to the system for the incoming lease’s resource, the latest leases for each leaf resource (since these can be different depending on the types of commands sent). This information can be used to react to the lease failure within the client application. diff --git a/docs/concepts/resource_tree.png b/docs/concepts/resource_tree.png new file mode 100644 index 000000000..5a69ca184 Binary files /dev/null and b/docs/concepts/resource_tree.png differ diff --git a/docs/concepts/robot_services.md b/docs/concepts/robot_services.md index 4d64b2791..c40ae56ba 100644 --- a/docs/concepts/robot_services.md +++ b/docs/concepts/robot_services.md @@ -72,7 +72,7 @@ The robot state service also tracks different parameters for the robot and this Spot can have many different image sources, including the cameras on the base platform or any other payloads which implement the image service proto definitions, like Spot CAM. The image service provides a way to list all these different sources using the `ListImageSources` RPC and then query the sources for their images with the `GetImage` RPC. -Images can be regular pixel-based visual images where the data value corresponds to the greyscale (or color) intensity. They can also be depth images where the data value corresponds to the depth measured from the camera sensor. To align depth data with visual image data, use the `depth_in_visual_frame` sources, which reprojects the depth onto the same projection as the visual image. +Images can be regular pixel-based visual images where the data value corresponds to the greyscale (or color) intensity. They can also be depth images where the data value corresponds to the depth measured from the camera sensor. To align depth data with visual image data, use the `depth_in_visual_frame` sources ([example code](../../python/examples/get_depth_plus_visual_image/README.md)), which reprojects the depth onto the same projection as the visual image. Since an image can be a lot of data, there are also different types of encodings and compression schemes that the image can be transmitted as to reduce the size of the data sent over the wire. The image service offers a `Format` field to describe the encoding of the image: diff --git a/docs/html/.buildinfo b/docs/html/.buildinfo index 41ec2bfc9..b943f295a 100644 --- a/docs/html/.buildinfo +++ b/docs/html/.buildinfo @@ -1,4 +1,4 @@ # Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: f3466e6a0c1cc35a831b2e5ec3f8a5f2 +config: a38894f88ac7eaa19d875d76f307766a tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/html/README.html b/docs/html/README.html index e21d82b97..28763dc5c 100644 --- a/docs/html/README.html +++ b/docs/html/README.html @@ -8,7 +8,7 @@ - Spot SDK — Spot 2.3.5 documentation + Spot SDK — Spot 3.0.0 documentation @@ -68,7 +68,7 @@
- 2.3.5 + 3.0.0
@@ -101,6 +101,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -244,6 +270,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -267,12 +294,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -320,6 +349,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -343,6 +373,8 @@
  • Choreography
  • @@ -376,6 +408,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -402,6 +436,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -409,6 +445,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -428,6 +466,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -445,6 +484,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -462,6 +502,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -565,7 +606,7 @@

    Spot SDKSpot API protocol definition. This reference guide covers the details of the protocol applications used to communicate to Spot. Application developers who wish to use a language other than Python can implement clients that speak the protocol.

  • Spot SDK Repository. The GitHub repo where all of the Spot SDK code is hosted.

  • -

    This is version 2.3.5 of the SDK. Please review the Release Notes to see what has changed.

    +

    This is version 3.0.0 of the SDK. Please review the Release Notes to see what has changed.

    Contents

    diff --git a/docs/html/_images/Goto.gif b/docs/html/_images/Goto.gif new file mode 100644 index 000000000..e4d21b733 Binary files /dev/null and b/docs/html/_images/Goto.gif differ diff --git a/docs/html/_images/adding_modes.png b/docs/html/_images/adding_modes.png new file mode 100644 index 000000000..86eca4bd4 Binary files /dev/null and b/docs/html/_images/adding_modes.png differ diff --git a/docs/html/_images/animation_upload_dialog.png b/docs/html/_images/animation_upload_dialog.png new file mode 100644 index 000000000..a85bf7f43 Binary files /dev/null and b/docs/html/_images/animation_upload_dialog.png differ diff --git a/docs/html/_images/choreography_interface.png b/docs/html/_images/choreography_interface.png new file mode 100644 index 000000000..632ceda40 Binary files /dev/null and b/docs/html/_images/choreography_interface.png differ diff --git a/docs/html/_images/choreography_interface_robot_management.png b/docs/html/_images/choreography_interface_robot_management.png new file mode 100644 index 000000000..69d223457 Binary files /dev/null and b/docs/html/_images/choreography_interface_robot_management.png differ diff --git a/docs/html/_images/compute_stand_position_diagram.svg b/docs/html/_images/compute_stand_position_diagram.svg new file mode 100644 index 000000000..8ae9110de --- /dev/null +++ b/docs/html/_images/compute_stand_position_diagram.svg @@ -0,0 +1,723 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/html/_images/directed_exploration_1.png b/docs/html/_images/directed_exploration_1.png new file mode 100644 index 000000000..f9b4e3023 Binary files /dev/null and b/docs/html/_images/directed_exploration_1.png differ diff --git a/docs/html/_images/directed_exploration_2.png b/docs/html/_images/directed_exploration_2.png new file mode 100644 index 000000000..11b857852 Binary files /dev/null and b/docs/html/_images/directed_exploration_2.png differ diff --git a/docs/html/_images/directed_exploration_3.png b/docs/html/_images/directed_exploration_3.png new file mode 100644 index 000000000..37162a86d Binary files /dev/null and b/docs/html/_images/directed_exploration_3.png differ diff --git a/docs/html/_images/example_image.jpg b/docs/html/_images/example_image.jpg new file mode 100644 index 000000000..e7d22551b Binary files /dev/null and b/docs/html/_images/example_image.jpg differ diff --git a/docs/html/_images/extract_cloud.png b/docs/html/_images/extract_cloud.png new file mode 100644 index 000000000..f08180399 Binary files /dev/null and b/docs/html/_images/extract_cloud.png differ diff --git a/docs/html/_images/fontawesome-webfont.svg b/docs/html/_images/fontawesome-webfont.svg new file mode 100644 index 000000000..855c845e5 --- /dev/null +++ b/docs/html/_images/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/html/_images/house_plans.png b/docs/html/_images/house_plans.png new file mode 100644 index 000000000..4331d7d1f Binary files /dev/null and b/docs/html/_images/house_plans.png differ diff --git a/docs/html/_images/image2.png b/docs/html/_images/image2.png deleted file mode 100644 index 56d1cd8d7..000000000 Binary files a/docs/html/_images/image2.png and /dev/null differ diff --git a/docs/html/_images/image3.png b/docs/html/_images/image3.png deleted file mode 100644 index ba52ea61f..000000000 Binary files a/docs/html/_images/image3.png and /dev/null differ diff --git a/docs/html/_images/image4.png b/docs/html/_images/image4.png deleted file mode 100644 index fbfe8ac13..000000000 Binary files a/docs/html/_images/image4.png and /dev/null differ diff --git a/docs/html/_images/image5.png b/docs/html/_images/image5.png deleted file mode 100644 index 8045446de..000000000 Binary files a/docs/html/_images/image5.png and /dev/null differ diff --git a/docs/html/_images/image6.png b/docs/html/_images/image6.png deleted file mode 100644 index c6315014a..000000000 Binary files a/docs/html/_images/image6.png and /dev/null differ diff --git a/docs/html/_images/image1.png b/docs/html/_images/joystick_help.png similarity index 100% rename from docs/html/_images/image1.png rename to docs/html/_images/joystick_help.png diff --git a/docs/html/_images/lease_example.png b/docs/html/_images/lease_example.png new file mode 100644 index 000000000..77fa1db08 Binary files /dev/null and b/docs/html/_images/lease_example.png differ diff --git a/docs/html/_images/log_buttons.png b/docs/html/_images/log_buttons.png new file mode 100644 index 000000000..e5bbcdc00 Binary files /dev/null and b/docs/html/_images/log_buttons.png differ diff --git a/docs/html/_images/main_image2.png b/docs/html/_images/main_image2.png deleted file mode 100644 index c1b58c26c..000000000 Binary files a/docs/html/_images/main_image2.png and /dev/null differ diff --git a/docs/html/_images/main_image4.png b/docs/html/_images/main_image4.png deleted file mode 100644 index d7ab0343e..000000000 Binary files a/docs/html/_images/main_image4.png and /dev/null differ diff --git a/docs/html/_images/multi_tracked_dance.png b/docs/html/_images/multi_tracked_dance.png new file mode 100644 index 000000000..4bfd6ea99 Binary files /dev/null and b/docs/html/_images/multi_tracked_dance.png differ diff --git a/docs/html/_images/network_compute_diagram.svg b/docs/html/_images/network_compute_diagram.svg new file mode 100644 index 000000000..e00ba0faf --- /dev/null +++ b/docs/html/_images/network_compute_diagram.svg @@ -0,0 +1,832 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + image + + network_compute_server.py + + + + + + Compute Thread + + + + GRPC Server + + + + Python Queue + + + bouding boxes, scorees, labeles bouding boxes, scores, labeles bouding boxes, scorees, labeles + + + + fetch.py + "please take a photo and run the dogtoy model on it" + image & bounding boxes + + + 1. Get image and bounding boxes + 2. Request Pick Up + + + + diff --git a/docs/html/_images/optimized_anchoring.png b/docs/html/_images/optimized_anchoring.png new file mode 100644 index 000000000..8c11cf9d7 Binary files /dev/null and b/docs/html/_images/optimized_anchoring.png differ diff --git a/docs/html/_images/optimized_anchoring_viewer.png b/docs/html/_images/optimized_anchoring_viewer.png new file mode 100644 index 000000000..c8d10740e Binary files /dev/null and b/docs/html/_images/optimized_anchoring_viewer.png differ diff --git a/docs/html/_images/preview_move_button.png b/docs/html/_images/preview_move_button.png new file mode 100644 index 000000000..95222fd22 Binary files /dev/null and b/docs/html/_images/preview_move_button.png differ diff --git a/docs/html/_images/rails-image3.png b/docs/html/_images/rails-image3.png index 6181f7d95..81f96bd3a 100644 Binary files a/docs/html/_images/rails-image3.png and b/docs/html/_images/rails-image3.png differ diff --git a/docs/html/_images/rails3.png b/docs/html/_images/rails3.png index fa2abc1c9..b0931ee0b 100644 Binary files a/docs/html/_images/rails3.png and b/docs/html/_images/rails3.png differ diff --git a/docs/html/_images/red_slider.png b/docs/html/_images/red_slider.png new file mode 100644 index 000000000..581f7edc4 Binary files /dev/null and b/docs/html/_images/red_slider.png differ diff --git a/docs/html/_images/resource_tree.png b/docs/html/_images/resource_tree.png new file mode 100644 index 000000000..5a69ca184 Binary files /dev/null and b/docs/html/_images/resource_tree.png differ diff --git a/docs/html/_images/robot_back_at_start.png b/docs/html/_images/robot_back_at_start.png new file mode 100644 index 000000000..bea92c405 Binary files /dev/null and b/docs/html/_images/robot_back_at_start.png differ diff --git a/docs/html/_images/robot_management.png b/docs/html/_images/robot_management.png new file mode 100644 index 000000000..be5bac5b5 Binary files /dev/null and b/docs/html/_images/robot_management.png differ diff --git a/docs/html/_images/rotation_vectors.svg b/docs/html/_images/rotation_vectors.svg new file mode 100644 index 000000000..32aeb375c --- /dev/null +++ b/docs/html/_images/rotation_vectors.svg @@ -0,0 +1,28556 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + xhat + + + + + + zhat + + + diff --git a/docs/html/_images/running_man.png b/docs/html/_images/running_man.png new file mode 100644 index 000000000..cf2409041 Binary files /dev/null and b/docs/html/_images/running_man.png differ diff --git a/docs/html/_images/topo_processing_example.svg b/docs/html/_images/topo_processing_example.svg new file mode 100644 index 000000000..63d4b09e6 --- /dev/null +++ b/docs/html/_images/topo_processing_example.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/html/_images/topologies.png b/docs/html/_images/topologies.png new file mode 100644 index 000000000..3da1fe508 Binary files /dev/null and b/docs/html/_images/topologies.png differ diff --git a/docs/html/_images/main_image1.png b/docs/html/_images/tracks_labeled.png similarity index 100% rename from docs/html/_images/main_image1.png rename to docs/html/_images/tracks_labeled.png diff --git a/docs/html/_images/transition_error.png b/docs/html/_images/transition_error.png new file mode 100644 index 000000000..4d6e4930d Binary files /dev/null and b/docs/html/_images/transition_error.png differ diff --git a/docs/html/_images/treehouse_anchoring.png b/docs/html/_images/treehouse_anchoring.png new file mode 100644 index 000000000..8670996c4 Binary files /dev/null and b/docs/html/_images/treehouse_anchoring.png differ diff --git a/docs/html/_images/treehouse_ko.png b/docs/html/_images/treehouse_ko.png new file mode 100644 index 000000000..8613538a1 Binary files /dev/null and b/docs/html/_images/treehouse_ko.png differ diff --git a/docs/html/_modules/bosdyn/bddf/base_data_reader.html b/docs/html/_modules/bosdyn/bddf/base_data_reader.html index 4b4dc64a2..00466fb01 100644 --- a/docs/html/_modules/bosdyn/bddf/base_data_reader.html +++ b/docs/html/_modules/bosdyn/bddf/base_data_reader.html @@ -7,7 +7,7 @@ - bosdyn.bddf.base_data_reader — Spot 2.3.5 documentation + bosdyn.bddf.base_data_reader — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -564,12 +605,20 @@

    Source code for bosdyn.bddf.base_data_reader

    [docs]class BaseDataReader:  # pylint: disable=too-many-instance-attributes
         """Shared parent class for DataReader and StreamedDataReader."""
     
    -    def __init__(self, outfile):
    +    def __init__(self, infile=None, filename=None):
             """
    +        At least one of the following arguments must be specified.
    +
             Args:
    -         outfile:      binary file-like object for reading (e.g., from open(fname, "rb")).
    +         infile:      binary file-like object for reading (e.g., from open(fname, "rb")).
    +         filename:    path of input file, if applicable.
             """
    -        self._file = outfile
    +        self._file = infile
    +        self._filename = filename
    +        if not self._file:
    +            if not self._filename:
    +                raise ValueError("One of infile or filename must be specified")
    +            self._file = open(self._filename, 'rb')
             self._file_descriptor = None
             self._spec_index = None
             self._index_offset = None
    @@ -579,6 +628,11 @@ 

    Source code for bosdyn.bddf.base_data_reader

    self._file_index = None
             self._read_header()
     
    +    @property
    +    def filename(self):
    +        """Return input file name, if specified, or None if not."""
    +        return self._filename
    +
         @property
         def file_descriptor(self):
             """Return the file descriptor from the start of the file/stream."""
    diff --git a/docs/html/_modules/bosdyn/bddf/block_writer.html b/docs/html/_modules/bosdyn/bddf/block_writer.html
    index e7eee0c82..67b4c7e60 100644
    --- a/docs/html/_modules/bosdyn/bddf/block_writer.html
    +++ b/docs/html/_modules/bosdyn/bddf/block_writer.html
    @@ -7,7 +7,7 @@
       
       
       
    -  bosdyn.bddf.block_writer — Spot 2.3.5 documentation
    +  bosdyn.bddf.block_writer — Spot 3.0.0 documentation
       
     
       
    @@ -66,7 +66,7 @@
                 
                 
                   
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/bddf/bosdyn.html b/docs/html/_modules/bosdyn/bddf/bosdyn.html index 037f7bcdb..0be41e9a2 100644 --- a/docs/html/_modules/bosdyn/bddf/bosdyn.html +++ b/docs/html/_modules/bosdyn/bddf/bosdyn.html @@ -7,7 +7,7 @@ - bosdyn.bddf.bosdyn — Spot 2.3.5 documentation + bosdyn.bddf.bosdyn — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/bddf/common.html b/docs/html/_modules/bosdyn/bddf/common.html index 3a085f6e0..47660e59a 100644 --- a/docs/html/_modules/bosdyn/bddf/common.html +++ b/docs/html/_modules/bosdyn/bddf/common.html @@ -7,7 +7,7 @@ - bosdyn.bddf.common — Spot 2.3.5 documentation + bosdyn.bddf.common — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/bddf/data_reader.html b/docs/html/_modules/bosdyn/bddf/data_reader.html index e8204bd6f..4fb0b1c78 100644 --- a/docs/html/_modules/bosdyn/bddf/data_reader.html +++ b/docs/html/_modules/bosdyn/bddf/data_reader.html @@ -7,7 +7,7 @@ - bosdyn.bddf.data_reader — Spot 2.3.5 documentation + bosdyn.bddf.data_reader — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -565,12 +606,15 @@

    Source code for bosdyn.bddf.data_reader

         Methods raise ParseError if there is a problem with the format of the file.
         """
     
    -    def __init__(self, outfile):
    +    def __init__(self, infile=None, filename=None):
             """
    +        At least one of the following arguments must be specified.
    +
             Args:
    -         outfile:      binary file-like object for reading (e.g., from open(fname, "rb")).
    +         infile:      binary file-like object for reading (e.g., from open(fname, "rb")).
    +         filename:    path of input file, if applicable.
             """
    -        super(DataReader, self).__init__(outfile)
    +        super(DataReader, self).__init__(infile, filename)
             self._series_index_to_descriptor = {}
             self._series_index_to_block_index = {}  # {series_index -> SeriesBlockIndex}
             self._read_index()
    diff --git a/docs/html/_modules/bosdyn/bddf/data_writer.html b/docs/html/_modules/bosdyn/bddf/data_writer.html
    index 2c0507453..94112204a 100644
    --- a/docs/html/_modules/bosdyn/bddf/data_writer.html
    +++ b/docs/html/_modules/bosdyn/bddf/data_writer.html
    @@ -7,7 +7,7 @@
       
       
       
    -  bosdyn.bddf.data_writer — Spot 2.3.5 documentation
    +  bosdyn.bddf.data_writer — Spot 3.0.0 documentation
       
     
       
    @@ -66,7 +66,7 @@
                 
                 
                   
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -555,8 +596,9 @@

    Source code for bosdyn.bddf.data_writer

     
     import bosdyn.api.bddf_pb2 as bddf
     
    -from . block_writer import BlockWriter
    -from . file_indexer import FileIndexer
    +from .block_writer import BlockWriter
    +from .file_indexer import FileIndexer
    +
     
     
    [docs]class DataWriter: """Class for writing data to a file.""" diff --git a/docs/html/_modules/bosdyn/bddf/file_indexer.html b/docs/html/_modules/bosdyn/bddf/file_indexer.html index e75c908bd..6b66450cc 100644 --- a/docs/html/_modules/bosdyn/bddf/file_indexer.html +++ b/docs/html/_modules/bosdyn/bddf/file_indexer.html @@ -7,7 +7,7 @@ - bosdyn.bddf.file_indexer — Spot 2.3.5 documentation + bosdyn.bddf.file_indexer — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/bddf/grpc_proto_reader.html b/docs/html/_modules/bosdyn/bddf/grpc_proto_reader.html index 37f448c45..faf6d911d 100644 --- a/docs/html/_modules/bosdyn/bddf/grpc_proto_reader.html +++ b/docs/html/_modules/bosdyn/bddf/grpc_proto_reader.html @@ -7,7 +7,7 @@ - bosdyn.bddf.grpc_proto_reader — Spot 2.3.5 documentation + bosdyn.bddf.grpc_proto_reader — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/bddf/grpc_reader.html b/docs/html/_modules/bosdyn/bddf/grpc_reader.html index 85f452448..0428130f3 100644 --- a/docs/html/_modules/bosdyn/bddf/grpc_reader.html +++ b/docs/html/_modules/bosdyn/bddf/grpc_reader.html @@ -7,7 +7,7 @@ - bosdyn.bddf.grpc_reader — Spot 2.3.5 documentation + bosdyn.bddf.grpc_reader — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -569,8 +610,7 @@

    Source code for bosdyn.bddf.grpc_reader

             self._service_name_to_reader = {}
             self._series_index_to_reader = {}
             proto_name_to_class = {
    -            proto_class.DESCRIPTOR.full_name: proto_class
    -            for proto_class in protobuf_classes
    +            proto_class.DESCRIPTOR.full_name: proto_class for proto_class in protobuf_classes
             }
             self._proto_name_to_reader = {}
             for series_index, series_identifier in enumerate(data_reader.file_index.series_identifiers):
    diff --git a/docs/html/_modules/bosdyn/bddf/grpc_service_reader.html b/docs/html/_modules/bosdyn/bddf/grpc_service_reader.html
    index 295391cf4..cee97dee9 100644
    --- a/docs/html/_modules/bosdyn/bddf/grpc_service_reader.html
    +++ b/docs/html/_modules/bosdyn/bddf/grpc_service_reader.html
    @@ -7,7 +7,7 @@
       
       
       
    -  bosdyn.bddf.grpc_service_reader — Spot 2.3.5 documentation
    +  bosdyn.bddf.grpc_service_reader — Spot 3.0.0 documentation
       
     
       
    @@ -66,7 +66,7 @@
                 
                 
                   
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/bddf/grpc_service_writer.html b/docs/html/_modules/bosdyn/bddf/grpc_service_writer.html index 6cd499e0b..6c08015d3 100644 --- a/docs/html/_modules/bosdyn/bddf/grpc_service_writer.html +++ b/docs/html/_modules/bosdyn/bddf/grpc_service_writer.html @@ -7,7 +7,7 @@ - bosdyn.bddf.grpc_service_writer — Spot 2.3.5 documentation + bosdyn.bddf.grpc_service_writer — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/bddf/message_reader.html b/docs/html/_modules/bosdyn/bddf/message_reader.html new file mode 100644 index 000000000..ab5970625 --- /dev/null +++ b/docs/html/_modules/bosdyn/bddf/message_reader.html @@ -0,0 +1,733 @@ + + + + + + + + + + bosdyn.bddf.message_reader — Spot 3.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + + + +
    + +
    + + + + + + + + + + + + + + + + + +
    + +
      + +
    • »
    • + +
    • Module code »
    • + +
    • bosdyn.bddf.message_reader
    • + + +
    • + +
    • + +
    + + +
    +
    +
    +
    + +

    Source code for bosdyn.bddf.message_reader

    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""A class for reading message data from a DataFile."""
    +
    +from .common import PROTOBUF_CONTENT_TYPE
    +
    +
    +
    [docs]class MessageReader: + """A class for reading message data from a DataFile. + + Methods raise ParseError if there is a problem with the format of the file. + """ + + def __init__(self, data_reader, require_protobuf=False): + self._data_reader = data_reader + self._channel_name_to_series_descriptor = {} + self._channel_name_to_series_index = {} + for series_index, series_identifier in enumerate(data_reader.file_index.series_identifiers): + try: + channel_name = series_identifier.spec["bosdyn:channel"] + except KeyError: + continue # Not a protobuf series + series_descriptor = self._data_reader.series_descriptor(series_index) + if series_descriptor.WhichOneof("DataType") != "message_type": + continue # Not a protobuf series + message_type = series_descriptor.message_type + if require_protobuf and message_type.content_type != PROTOBUF_CONTENT_TYPE: + continue # Not a protobuf series + self._channel_name_to_series_descriptor[channel_name] = series_descriptor + self._channel_name_to_series_index[channel_name] = series_descriptor.series_index + + @property + def data_reader(self): + """Return underlying DataReader this object is using.""" + return self._data_reader + + @property + def channel_name_to_series_decriptor(self): + """Return a mapping of {chanel name -> series descriptor} for message series.""" + return self._channel_name_to_series_descriptor + +
    [docs] def series_index(self, channel_name, message_type=None): + """Return series index (int) to access SeriesDescriptors and messages. + + Args: + channel_name: name of the channel of messages. + message_type: specify message type, if channel may have multiple + kinds of messages (optional) + """ + if message_type is None: + return self._channel_name_to_series_index[channel_name] + + for series_index, series_identifier in enumerate( + self._data_reader.file_index.series_identifiers): + try: + series_channel = series_identifier.spec["bosdyn:channel"] + except KeyError: + continue # Not a protobuf series + if series_channel != channel_name: + continue + series_descriptor = self._data_reader.series_descriptor(series_index) + if series_descriptor.WhichOneof("DataType") != "message_type": + continue # Not a protobuf series + if message_type == series_descriptor.message_type.type_name: + return series_index + + raise KeyError("No series with channel_name={} and message_type={}".format( + channel_name, message_type))
    + +
    [docs] def series_index_to_descriptor(self, series_index): + """Given a series index, return the associated SeriesDescriptor + + Args: + series_index: index (int) from the series_index() call + """ + return self._data_reader.file_index.series_descriptor(series_index)
    + +
    [docs] def get_blob(self, series_index, index_in_series): + """Return binary data from message stored in the file. + + Args: + series_index: index (int) from the series_index() call + index_in_series: the index of the message within the series + + Returns: DataTypeDescriptor for channel, timestamp_nsec (int), binary data + """ + return self._data_reader.read(series_index, index_in_series)
    +
    + +
    + +
    + +
    +
    + +
    + +
    + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/html/_modules/bosdyn/bddf/pod_series_reader.html b/docs/html/_modules/bosdyn/bddf/pod_series_reader.html index 46b30a403..5551cfbf7 100644 --- a/docs/html/_modules/bosdyn/bddf/pod_series_reader.html +++ b/docs/html/_modules/bosdyn/bddf/pod_series_reader.html @@ -7,7 +7,7 @@ - bosdyn.bddf.pod_series_reader — Spot 2.3.5 documentation + bosdyn.bddf.pod_series_reader — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/bddf/pod_series_writer.html b/docs/html/_modules/bosdyn/bddf/pod_series_writer.html index a35300d5b..9436c5d0c 100644 --- a/docs/html/_modules/bosdyn/bddf/pod_series_writer.html +++ b/docs/html/_modules/bosdyn/bddf/pod_series_writer.html @@ -7,7 +7,7 @@ - bosdyn.bddf.pod_series_writer — Spot 2.3.5 documentation + bosdyn.bddf.pod_series_writer — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/bddf/protobuf_channel_reader.html b/docs/html/_modules/bosdyn/bddf/protobuf_channel_reader.html index f6bfa6aaa..3b4ec65b9 100644 --- a/docs/html/_modules/bosdyn/bddf/protobuf_channel_reader.html +++ b/docs/html/_modules/bosdyn/bddf/protobuf_channel_reader.html @@ -7,7 +7,7 @@ - bosdyn.bddf.protobuf_channel_reader — Spot 2.3.5 documentation + bosdyn.bddf.protobuf_channel_reader — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -561,7 +602,8 @@

    Source code for bosdyn.bddf.protobuf_channel_reader

    self._protobuf_reader = protobuf_reader self._protobuf_type = protobuf_type self._channel_name = channel_name or protobuf_type.DESCRIPTOR.full_name - self._series_index = self._protobuf_reader.series_index(self._channel_name) + self._series_index = self._protobuf_reader.series_index( + self._channel_name, message_type=protobuf_type.DESCRIPTOR.full_name) self._descriptor = None self._num_messages = None @@ -591,7 +633,26 @@

    Source code for bosdyn.bddf.protobuf_channel_reader

    _desc, timestamp, msg = self._protobuf_reader.get_message(self._series_index, self._protobuf_type, index_in_series) - return timestamp, msg
    + return timestamp, msg
    + + + def __iter__(self): + return ProtobufChannelReader.Iterator(self) + +
    [docs] class Iterator: # pylint: disable=too-few-public-methods + """Iterator over messages from a ProtobufChannelReader""" + + def __init__(self, channel_reader): + self._channel_reader = channel_reader + self._index = 0 + + def __next__(self): + """Returns the next vlue.""" + if self._index >= self._channel_reader.num_messages: + raise StopIteration + msg = self._channel_reader.get_message(self._index) + self._index += 1 + return msg
    diff --git a/docs/html/_modules/bosdyn/bddf/protobuf_reader.html b/docs/html/_modules/bosdyn/bddf/protobuf_reader.html index bdf9a0031..26a802c0e 100644 --- a/docs/html/_modules/bosdyn/bddf/protobuf_reader.html +++ b/docs/html/_modules/bosdyn/bddf/protobuf_reader.html @@ -7,7 +7,7 @@ - bosdyn.bddf.protobuf_reader — Spot 2.3.5 documentation + bosdyn.bddf.protobuf_reader — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -552,54 +593,17 @@

    Source code for bosdyn.bddf.protobuf_reader

     # Development Kit License (20191101-BDSDK-SL).
     
     """A class for reading Protobuf data from a DataFile."""
    -
    -from .common import PROTOBUF_CONTENT_TYPE
    +from .message_reader import MessageReader
     
     
    -
    [docs]class ProtobufReader: +
    [docs]class ProtobufReader(MessageReader): """A class for reading Protobuf data from a DataFile. Methods raise ParseError if there is a problem with the format of the file. """ def __init__(self, data_reader): - self._data_reader = data_reader - self._channel_name_to_series_descriptor = {} - self._channel_name_to_series_index = {} - for series_index, series_identifier in enumerate(data_reader.file_index.series_identifiers): - try: - channel_name = series_identifier.spec["bosdyn:channel"] - except KeyError: - continue # Not a protobuf series - series_descriptor = self._data_reader.series_descriptor(series_index) - if series_descriptor.WhichOneof("DataType") != "message_type": - continue # Not a protobuf series - message_type = series_descriptor.message_type - if message_type.content_type != PROTOBUF_CONTENT_TYPE: - continue # Not a protobuf series - self._channel_name_to_series_descriptor[channel_name] = series_descriptor - self._channel_name_to_series_index[channel_name] = series_descriptor.series_index - - @property - def data_reader(self): - """Return underlying DataReader this object is using.""" - return self._data_reader - -
    [docs] def series_index(self, channel_name): - """Return the series index (int) by which SeriesDescriptors and messages can be accessed. - - Args: - channel_name: name of the channel of messages. - """ - return self._channel_name_to_series_index[channel_name]
    - -
    [docs] def series_index_to_descriptor(self, series_index): - """Given a series index, return the associated SeriesDescriptor - - Args: - series_index: index (int) from the series_index() call - """ - return self._data_reader.file_index.series_descriptor(series_index)
    + super(ProtobufReader, self).__init__(data_reader, require_protobuf=True)
    [docs] def get_message(self, series_index, protobuf_type, index_in_series): """Return a deserialized protobuf from bytes stored in the file. @@ -611,7 +615,7 @@

    Source code for bosdyn.bddf.protobuf_reader

     
             Returns: DataTypeDescriptor for channel, timestamp_nsec (int), deserialized protobuf object
             """
    -        desc, timestamp_nsec, data = self._data_reader.read(series_index, index_in_series)
    +        desc, timestamp_nsec, data = self.get_blob(series_index, index_in_series)
             protobuf = protobuf_type()
             protobuf.ParseFromString(data)
             return desc, timestamp_nsec, protobuf
    diff --git a/docs/html/_modules/bosdyn/bddf/protobuf_series_writer.html b/docs/html/_modules/bosdyn/bddf/protobuf_series_writer.html index 1836bafc6..bf85e1145 100644 --- a/docs/html/_modules/bosdyn/bddf/protobuf_series_writer.html +++ b/docs/html/_modules/bosdyn/bddf/protobuf_series_writer.html @@ -7,7 +7,7 @@ - bosdyn.bddf.protobuf_series_writer — Spot 2.3.5 documentation + bosdyn.bddf.protobuf_series_writer — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -553,8 +594,9 @@

    Source code for bosdyn.bddf.protobuf_series_writer

    """Class for registering a series which stores protobuf messages in a message series.""" -from . bosdyn import MessageChannel -from . common import PROTOBUF_CONTENT_TYPE +from .bosdyn import MessageChannel +from .common import PROTOBUF_CONTENT_TYPE +
    [docs]class ProtobufSeriesWriter: # pylint: disable=too-many-instance-attributes """A class for registering a series which stores protobuf messages in a message series. diff --git a/docs/html/_modules/bosdyn/bddf/stream_data_reader.html b/docs/html/_modules/bosdyn/bddf/stream_data_reader.html index 9c328a087..93d9727ff 100644 --- a/docs/html/_modules/bosdyn/bddf/stream_data_reader.html +++ b/docs/html/_modules/bosdyn/bddf/stream_data_reader.html @@ -7,7 +7,7 @@ - bosdyn.bddf.stream_data_reader — Spot 2.3.5 documentation + bosdyn.bddf.stream_data_reader — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/choreography/client/animation_file_conversion_helpers.html b/docs/html/_modules/bosdyn/choreography/client/animation_file_conversion_helpers.html new file mode 100644 index 000000000..fa3168676 --- /dev/null +++ b/docs/html/_modules/bosdyn/choreography/client/animation_file_conversion_helpers.html @@ -0,0 +1,1235 @@ + + + + + + + + + + bosdyn.choreography.client.animation_file_conversion_helpers — Spot 3.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + + + +
    + +
    + + + + + + + + + + + + + + + + + +
    + +
      + +
    • »
    • + +
    • Module code »
    • + +
    • bosdyn.choreography.client.animation_file_conversion_helpers
    • + + +
    • + +
    • + +
    + + +
    +
    +
    +
    + +

    Source code for bosdyn.choreography.client.animation_file_conversion_helpers

    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""A set helpers which convert specific lines from an animation
    +file into the animation-specific protobuf messages.
    +
    +NOTE: All of these helpers are to convert specific values read from a `cha`
    +file into fields within the choreography_sequence_pb2.Animation protobuf
    +message. They are used by the animation_file_to_proto.py file.
    +"""
    +
    +from bosdyn.api.spot import (choreography_sequence_pb2, choreography_service_pb2,
    +                             choreography_service_pb2_grpc)
    +
    +
    +
    [docs]def start_time_handler(val, animation_frame): + animation_frame.time = val + return animation_frame
    + + +
    [docs]def fl_angles_handler(vals, animation_frame): + animation_frame.legs.fl.joint_angles.hip_x = vals[0] + animation_frame.legs.fl.joint_angles.hip_y = vals[1] + animation_frame.legs.fl.joint_angles.knee = vals[2] + return animation_frame
    + + +
    [docs]def fr_angles_handler(vals, animation_frame): + animation_frame.legs.fr.joint_angles.hip_x = vals[0] + animation_frame.legs.fr.joint_angles.hip_y = vals[1] + animation_frame.legs.fr.joint_angles.knee = vals[2] + return animation_frame
    + + +
    [docs]def hl_angles_handler(vals, animation_frame): + animation_frame.legs.hl.joint_angles.hip_x = vals[0] + animation_frame.legs.hl.joint_angles.hip_y = vals[1] + animation_frame.legs.hl.joint_angles.knee = vals[2] + return animation_frame
    + + +
    [docs]def hr_angles_handler(vals, animation_frame): + animation_frame.legs.hr.joint_angles.hip_x = vals[0] + animation_frame.legs.hr.joint_angles.hip_y = vals[1] + animation_frame.legs.hr.joint_angles.knee = vals[2] + return animation_frame
    + + +
    [docs]def fl_pos_handler(vals, animation_frame): + animation_frame.legs.fl.foot_pos.x.value = vals[0] + animation_frame.legs.fl.foot_pos.y.value = vals[1] + animation_frame.legs.fl.foot_pos.z.value = vals[2] + return animation_frame
    + + +
    [docs]def fr_pos_handler(vals, animation_frame): + animation_frame.legs.fr.foot_pos.x.value = vals[0] + animation_frame.legs.fr.foot_pos.y.value = vals[1] + animation_frame.legs.fr.foot_pos.z.value = vals[2] + return animation_frame
    + + +
    [docs]def hl_pos_handler(vals, animation_frame): + animation_frame.legs.hl.foot_pos.x.value = vals[0] + animation_frame.legs.hl.foot_pos.y.value = vals[1] + animation_frame.legs.hl.foot_pos.z.value = vals[2] + return animation_frame
    + + +
    [docs]def hr_pos_handler(vals, animation_frame): + animation_frame.legs.hr.foot_pos.x.value = vals[0] + animation_frame.legs.hr.foot_pos.y.value = vals[1] + animation_frame.legs.hr.foot_pos.z.value = vals[2] + return animation_frame
    + + +
    [docs]def gripper_handler(val, animation_frame): + animation_frame.gripper.gripper_angle.value = val + return animation_frame
    + + +
    [docs]def fl_contact_handler(val, animation_frame): + animation_frame.legs.fl.stance.value = val + return animation_frame
    + + +
    [docs]def fr_contact_handler(val, animation_frame): + animation_frame.legs.fr.stance.value = val + return animation_frame
    + + +
    [docs]def hl_contact_handler(val, animation_frame): + animation_frame.legs.hl.stance.value = val + return animation_frame
    + + +
    [docs]def hr_contact_handler(val, animation_frame): + animation_frame.legs.hr.stance.value = val + return animation_frame
    + + +
    [docs]def sh0_handler(val, animation_frame): + animation_frame.arm.joint_angles.shoulder_0.value = val + return animation_frame
    + + +
    [docs]def sh1_handler(val, animation_frame): + animation_frame.arm.joint_angles.shoulder_1.value = val + return animation_frame
    + + +
    [docs]def el0_handler(val, animation_frame): + animation_frame.arm.joint_angles.elbow_0.value = val + return animation_frame
    + + +
    [docs]def el1_handler(val, animation_frame): + animation_frame.arm.joint_angles.elbow_1.value = val + return animation_frame
    + + +
    [docs]def wr0_handler(val, animation_frame): + animation_frame.arm.joint_angles.wrist_0.value = val + return animation_frame
    + + +
    [docs]def wr1_handler(val, animation_frame): + animation_frame.arm.joint_angles.wrist_1.value = val + return animation_frame
    + + +
    [docs]def fl_hx_handler(val, animation_frame): + animation_frame.legs.fl.joint_angles.hip_x = val + return animation_frame
    + + +
    [docs]def fl_hy_handler(val, animation_frame): + animation_frame.legs.fl.joint_angles.hip_y = val + return animation_frame
    + + +
    [docs]def fl_kn_handler(val, animation_frame): + animation_frame.legs.fl.joint_angles.knee = val + return animation_frame
    + + +
    [docs]def fr_hx_handler(val, animation_frame): + animation_frame.legs.fr.joint_angles.hip_x = val + return animation_frame
    + + +
    [docs]def fr_hy_handler(val, animation_frame): + animation_frame.legs.fr.joint_angles.hip_y = val + return animation_frame
    + + +
    [docs]def fr_kn_handler(val, animation_frame): + animation_frame.legs.fr.joint_angles.knee = val + return animation_frame
    + + +
    [docs]def hl_hx_handler(val, animation_frame): + animation_frame.legs.hl.joint_angles.hip_x = val + return animation_frame
    + + +
    [docs]def hl_hy_handler(val, animation_frame): + animation_frame.legs.hl.joint_angles.hip_y = val + return animation_frame
    + + +
    [docs]def hl_kn_handler(val, animation_frame): + animation_frame.legs.hl.joint_angles.knee = val + return animation_frame
    + + +
    [docs]def hr_hx_handler(val, animation_frame): + animation_frame.legs.hr.joint_angles.hip_x = val + return animation_frame
    + + +
    [docs]def hr_hy_handler(val, animation_frame): + animation_frame.legs.hr.joint_angles.hip_y = val + return animation_frame
    + + +
    [docs]def hr_kn_handler(val, animation_frame): + animation_frame.legs.hr.joint_angles.knee = val + return animation_frame
    + + +
    [docs]def fl_x_handler(val, animation_frame): + animation_frame.legs.fl.foot_pos.x.value = val + return animation_frame
    + + +
    [docs]def fl_y_handler(val, animation_frame): + animation_frame.legs.fl.foot_pos.y.value = val + return animation_frame
    + + +
    [docs]def fl_z_handler(val, animation_frame): + animation_frame.legs.fl.foot_pos.z.value = val + return animation_frame
    + + +
    [docs]def fr_x_handler(val, animation_frame): + animation_frame.legs.fr.foot_pos.x.value = val + return animation_frame
    + + +
    [docs]def fr_y_handler(val, animation_frame): + animation_frame.legs.fr.foot_pos.y.value = val + return animation_frame
    + + +
    [docs]def fr_z_handler(val, animation_frame): + animation_frame.legs.fr.foot_pos.z.value = val + return animation_frame
    + + +
    [docs]def hl_x_handler(val, animation_frame): + animation_frame.legs.hl.foot_pos.x.value = val + return animation_frame
    + + +
    [docs]def hl_y_handler(val, animation_frame): + animation_frame.legs.hl.foot_pos.y.value = val + return animation_frame
    + + +
    [docs]def hl_z_handler(val, animation_frame): + animation_frame.legs.hl.foot_pos.z.value = val + return animation_frame
    + + +
    [docs]def hr_x_handler(val, animation_frame): + animation_frame.legs.hr.foot_pos.x.value = val + return animation_frame
    + + +
    [docs]def hr_y_handler(val, animation_frame): + animation_frame.legs.hr.foot_pos.y.value = val + return animation_frame
    + + +
    [docs]def hr_z_handler(val, animation_frame): + animation_frame.legs.hr.foot_pos.z.value = val + return animation_frame
    + + +
    [docs]def body_x_handler(val, animation_frame): + animation_frame.body.body_pos.x.value = val + return animation_frame
    + + +
    [docs]def body_y_handler(val, animation_frame): + animation_frame.body.body_pos.y.value = val + return animation_frame
    + + +
    [docs]def body_z_handler(val, animation_frame): + animation_frame.body.body_pos.z.value = val + return animation_frame
    + + +
    [docs]def com_x_handler(val, animation_frame): + animation_frame.body.com_pos.x.value = val + return animation_frame
    + + +
    [docs]def com_y_handler(val, animation_frame): + animation_frame.body.com_pos.y.value = val + return animation_frame
    + + +
    [docs]def com_z_handler(val, animation_frame): + animation_frame.body.com_pos.z.value = val + return animation_frame
    + + +
    [docs]def body_quat_x_handler(val, animation_frame): + animation_frame.body.quaternion.x = val + return animation_frame
    + + +
    [docs]def body_quat_y_handler(val, animation_frame): + animation_frame.body.quaternion.y = val + return animation_frame
    + + +
    [docs]def body_quat_z_handler(val, animation_frame): + animation_frame.body.quaternion.z = val + return animation_frame
    + + +
    [docs]def body_quat_w_handler(val, animation_frame): + animation_frame.body.quaternion.w = val + return animation_frame
    + + +
    [docs]def body_roll_handler(val, animation_frame): + animation_frame.body.euler_angles.roll.value = val + return animation_frame
    + + +
    [docs]def body_pitch_handler(val, animation_frame): + animation_frame.body.euler_angles.pitch.value = val + return animation_frame
    + + +
    [docs]def body_yaw_handler(val, animation_frame): + animation_frame.body.euler_angles.yaw.value = val + return animation_frame
    + + +
    [docs]def body_pos_handler(vals, animation_frame): + animation_frame.body.body_pos.x.value = vals[0] + animation_frame.body.body_pos.y.value = vals[1] + animation_frame.body.body_pos.z.value = vals[2] + return animation_frame
    + + +
    [docs]def com_pos_handler(vals, animation_frame): + animation_frame.body.com_pos.x.value = vals[0] + animation_frame.body.com_pos.y.value = vals[1] + animation_frame.body.com_pos.z.value = vals[2] + return animation_frame
    + + +
    [docs]def body_euler_rpy_angles_handler(vals, animation_frame): + animation_frame.body.euler_angles.roll.value = vals[0] + animation_frame.body.euler_angles.pitch.value = vals[1] + animation_frame.body.euler_angles.yaw.value = vals[2] + return animation_frame
    + + +
    [docs]def body_quaternion_xyzw_handler(vals, animation_frame): + animation_frame.body.quaternion.x = vals[0] + animation_frame.body.quaternion.y = vals[1] + animation_frame.body.quaternion.z = vals[2] + animation_frame.body.quaternion.w = vals[3] + return animation_frame
    + + +
    [docs]def body_quaternion_wxyz_handler(vals, animation_frame): + animation_frame.body.quaternion.x = vals[1] + animation_frame.body.quaternion.y = vals[2] + animation_frame.body.quaternion.z = vals[3] + animation_frame.body.quaternion.w = vals[0] + return animation_frame
    + + +
    [docs]def leg_angles_handler(vals, animation_frame): + animation_frame.legs.fl.joint_angles.hip_x = vals[0] + animation_frame.legs.fl.joint_angles.hip_y = vals[1] + animation_frame.legs.fl.joint_angles.knee = vals[2] + animation_frame.legs.fr.joint_angles.hip_x = vals[3] + animation_frame.legs.fr.joint_angles.hip_y = vals[4] + animation_frame.legs.fr.joint_angles.knee = vals[5] + animation_frame.legs.hl.joint_angles.hip_x = vals[6] + animation_frame.legs.hl.joint_angles.hip_y = vals[7] + animation_frame.legs.hl.joint_angles.knee = vals[8] + animation_frame.legs.hr.joint_angles.hip_x = vals[9] + animation_frame.legs.hr.joint_angles.hip_y = vals[10] + animation_frame.legs.hr.joint_angles.knee = vals[11] + return animation_frame
    + + +
    [docs]def foot_pos_handler(vals, animation_frame): + animation_frame.legs.fl.foot_pos.x.value = vals[0] + animation_frame.legs.fl.foot_pos.y.value = vals[1] + animation_frame.legs.fl.foot_pos.z.value = vals[2] + animation_frame.legs.fr.foot_pos.x.value = vals[3] + animation_frame.legs.fr.foot_pos.y.value = vals[4] + animation_frame.legs.fr.foot_pos.z.value = vals[5] + animation_frame.legs.hl.foot_pos.x.value = vals[6] + animation_frame.legs.hl.foot_pos.y.value = vals[7] + animation_frame.legs.hl.foot_pos.z.value = vals[8] + animation_frame.legs.hr.foot_pos.x.value = vals[9] + animation_frame.legs.hr.foot_pos.y.value = vals[10] + animation_frame.legs.hr.foot_pos.z.value = vals[11] + return animation_frame
    + + +
    [docs]def contact_handler(vals, animation_frame): + animation_frame.legs.fl.stance.value = vals[0] + animation_frame.legs.fr.stance.value = vals[1] + animation_frame.legs.hl.stance.value = vals[2] + animation_frame.legs.hr.stance.value = vals[3] + return animation_frame
    + + +
    [docs]def arm_joints_handler(vals, animation_frame): + animation_frame.arm.joint_angles.shoulder_0.value = vals[0] + animation_frame.arm.joint_angles.shoulder_1.value = vals[1] + animation_frame.arm.joint_angles.elbow_0.value = vals[2] + animation_frame.arm.joint_angles.elbow_1.value = vals[3] + animation_frame.arm.joint_angles.wrist_0.value = vals[4] + animation_frame.arm.joint_angles.wrist_1.value = vals[5] + return animation_frame
    + + +
    [docs]def hand_x_handler(vals, animation_frame): + animation_frame.arm.hand_pose.position.x = vals[0] + return animation_frame
    + + +
    [docs]def hand_y_handler(vals, animation_frame): + animation_frame.arm.hand_pose.position.y = vals[0] + return animation_frame
    + + +
    [docs]def hand_z_handler(vals, animation_frame): + animation_frame.arm.hand_pose.position.z = vals[0] + return animation_frame
    + + +
    [docs]def hand_quat_x_handler(val, animation_frame): + animation_frame.arm.hand_pose.quaternion.x = val + return animation_frame
    + + +
    [docs]def hand_quat_y_handler(val, animation_frame): + animation_frame.arm.hand_pose.quaternion.y = val + return animation_frame
    + + +
    [docs]def hand_quat_z_handler(val, animation_frame): + animation_frame.arm.hand_pose.quaternion.z = val + return animation_frame
    + + +
    [docs]def hand_quat_w_handler(val, animation_frame): + animation_frame.arm.hand_pose.quaternion.w = val + return animation_frame
    + + +
    [docs]def hand_roll_handler(val, animation_frame): + animation_frame.arm.hand_pose.euler_angles.roll.value = val + return animation_frame
    + + +
    [docs]def hand_pitch_handler(val, animation_frame): + animation_frame.arm.hand_pose.euler_angles.pitch.value = val + return animation_frame
    + + +
    [docs]def hand_yaw_handler(val, animation_frame): + animation_frame.arm.hand_pose.euler_angles.yaw.value = val + return animation_frame
    + + +
    [docs]def hand_pos_handler(vals, animation_frame): + animation_frame.arm.hand_pose.position.x.value = vals[0] + animation_frame.arm.hand_pose.position.y.value = vals[1] + animation_frame.arm.hand_pose.position.z.value = vals[2] + return animation_frame
    + + +
    [docs]def hand_euler_rpy_angles_handler(vals, animation_frame): + animation_frame.arm.hand_pose.euler_angles.roll.value = vals[0] + animation_frame.arm.hand_pose.euler_angles.pitch.value = vals[1] + animation_frame.arm.hand_pose.euler_angles.yaw.value = vals[2] + return animation_frame
    + + +
    [docs]def hand_quaternion_xyzw_handler(vals, animation_frame): + animation_frame.arm.hand_pose.quaternion.x = vals[0] + animation_frame.arm.hand_pose.quaternion.y = vals[1] + animation_frame.arm.hand_pose.quaternion.z = vals[2] + animation_frame.arm.hand_pose.quaternion.w = vals[3] + return animation_frame
    + + +
    [docs]def hand_quaternion_wxyz_handler(vals, animation_frame): + animation_frame.arm.hand_pose.quaternion.x = vals[1] + animation_frame.arm.hand_pose.quaternion.y = vals[2] + animation_frame.arm.hand_pose.quaternion.z = vals[3] + animation_frame.arm.hand_pose.quaternion.w = vals[0] + return animation_frame
    + + +
    [docs]def controls_option(file_line_split, animation): + for track in file_line_split: + if track == "legs": + animation.proto.controls_legs = True + elif track == "arm": + animation.proto.controls_arm = True + elif track == "body": + animation.proto.controls_body = True + elif track == "gripper": + animation.proto.controls_gripper = True + elif track == "controls": + continue + else: + print("Unknown track name %s" % track) + return animation
    + + +
    [docs]def bpm_option(file_line_split, animation): + bpm = file_line_split[1] + animation.bpm = int(bpm) + return animation
    + + +
    [docs]def extendable_option(file_line_split, animation): + animation.proto.extendable = True + return animation
    + + +
    [docs]def truncatable_option(file_line_split, animation): + animation.proto.truncatable = True + return animation
    + + +
    [docs]def neutral_start_option(file_line_split, animation): + animation.proto.neutral_start = True + return animation
    + + +
    [docs]def precise_steps_option(file_line_split, animation): + animation.proto.precise_steps = True
    + +
    [docs]def precise_timing_option(file_line_split, animation): + animation.proto.precise_timing = True
    + +
    [docs]def no_looping_option(file_line_split, animation): + animation.proto.no_looping = True
    + +
    [docs]def arm_required_option(file_line_split, animation): + animation.proto.arm_required = True
    + +
    [docs]def arm_prohibited_option(file_line_split, animation): + animation.proto.arm_prohibited = True
    + +
    [docs]def track_swing_trajectories_option(file_line_split, animation): + animation.proto.track_swing_trajectories = True + return animation
    + +
    [docs]def assume_zero_roll_and_pitch_option(file_line_split, animation): + animation.proto.assume_zero_roll_and_pitch = True + return animation
    + +
    [docs]def track_hand_rt_body_option(file_line_split, animation): + animation.proto.track_hand_rt_body = True + return animation
    + + +
    [docs]def track_hand_rt_feet_option(file_line_split, animation): + animation.proto.track_hand_rt_feet = True + return animation
    + + +
    [docs]def arm_playback_option(file_line_split, animation): + playback = file_line_split[1] + if playback == "jointspace": + animation.proto.arm_playback = choreography_sequence_pb2.Animation.ARM_PLAYBACK_JOINTSPACE + elif playback == "workspace": + animation.proto.arm_playback = choreography_sequence_pb2.Animation.ARM_PLAYBACK_WORKSPACE + elif playback == "workspace_dance_frame": + animation.proto.arm_playback = choreography_sequence_pb2.Animation.ARM_PLAYBACK_WORKSPACE_DANCE_FRAME + else: + animation.proto.arm_playback = choreography_sequence_pb2.Animation.ARM_PLAYBACK_DEFAULT + print("Unknown arm playbak option %s" % playback) + return animation
    + + +
    [docs]def display_rgb_option(file_line_split, animation): + for i in range(1, 3): + animation.rgb[i - 1] = int(file_line_split[i]) + return animation
    + + +
    [docs]def frequency_option(file_line_split, animation): + freq = file_line_split[1] + animation.frequency = float(freq) + return animation
    + + +
    [docs]def retime_to_integer_slices_option(file_line_split, animation): + animation.proto.retime_to_integer_slices = True + return animation
    + + +
    [docs]def description_option(file_line_split, animation): + description = " ".join(file_line_split[1:]) + description = description.replace('"', '') # remove any quotation marks + animation.description = description + return animation
    +
    + +
    + +
    + +
    +
    + +
    + +
    + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/html/_modules/bosdyn/choreography/client/animation_file_to_proto.html b/docs/html/_modules/bosdyn/choreography/client/animation_file_to_proto.html new file mode 100644 index 000000000..8e9c12ceb --- /dev/null +++ b/docs/html/_modules/bosdyn/choreography/client/animation_file_to_proto.html @@ -0,0 +1,1214 @@ + + + + + + + + + + bosdyn.choreography.client.animation_file_to_proto — Spot 3.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + + + +
    + +
    + + + + + + + + + + + + + + + + + +
    + +
      + +
    • »
    • + +
    • Module code »
    • + +
    • bosdyn.choreography.client.animation_file_to_proto
    • + + +
    • + +
    • + +
    + + +
    +
    +
    +
    + +

    Source code for bosdyn.choreography.client.animation_file_to_proto

    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""A tool to convert animation files into protobuf messages which can be uploaded to the robot
    +and used within choreography sequences."""
    +
    +import argparse
    +import hashlib
    +import logging
    +import ntpath
    +import os
    +import sys
    +
    +from google.protobuf import text_format, wrappers_pb2
    +
    +import bosdyn.client
    +import bosdyn.client.util
    +from bosdyn.api.spot import choreography_sequence_pb2
    +from bosdyn.choreography.client.animation_file_conversion_helpers import *
    +
    +LOGGER = logging.getLogger(__name__)
    +
    +# The options keywords represent the first section of the file, and will be parsed into
    +# specific fields within the Animation proto.
    +OPTIONS_KEYWORDS_TO_FUNCTION = {
    +    "controls": controls_option,
    +    "bpm": bpm_option,
    +    "extendable": extendable_option,
    +    "truncatable": truncatable_option,
    +    "neutral_start": neutral_start_option,
    +    "precise_steps": precise_steps_option,
    +    "precise_timing": precise_timing_option,
    +    "no_looping": no_looping_option,
    +    "arm_required": arm_required_option,
    +    "arm_prohibited": arm_prohibited_option,
    +    "track_swing_trajectories": track_swing_trajectories_option,
    +    "assume_zero_roll_and_pitch": assume_zero_roll_and_pitch_option,
    +    "arm_playback": arm_playback_option,
    +    "display_rgb": display_rgb_option,
    +    "frequency": frequency_option,
    +    "retime_to_integer_slices": retime_to_integer_slices_option,
    +    "description": description_option,
    +}
    +
    +# The grouped headers represent animation keyframe values which can be used and specify multiple protobuf
    +# values for a single header keyword. For example, body_pos has x/y/z position values.
    +GROUPED_HEADERS = {
    +    "body_pos": (3, body_pos_handler),
    +    "com_pos": (3, com_pos_handler),
    +    "body_euler_rpy": (3, body_euler_rpy_angles_handler),
    +    "body_quat_xyzw": (4, body_quaternion_xyzw_handler),
    +    "body_quat_wxyz": (4, body_quaternion_wxyz_handler),
    +    "leg_joints": (12, leg_angles_handler),
    +    "foot_pos": (12, foot_pos_handler),
    +    "hand_pos": (3, hand_pos_handler),
    +    "hand_euler_rpy": (3, hand_euler_rpy_angles_handler),
    +    "hand_quat_xyzw": (4, hand_quaternion_xyzw_handler),
    +    "hand_quat_wxyz": (4, hand_quaternion_wxyz_handler),
    +    "contact": (4, contact_handler),
    +    "arm_joints": (6, arm_joints_handler),
    +    "fl_angles": (3, fl_angles_handler),
    +    "fr_angles": (3, fr_angles_handler),
    +    "hl_angles": (3, hl_angles_handler),
    +    "hr_angles": (3, hr_angles_handler),
    +    "fl_pos": (3, fl_pos_handler),
    +    "fr_pos": (3, fr_pos_handler),
    +    "hl_pos": (3, hl_pos_handler),
    +    "hr_pos": (3, hr_pos_handler),
    +}
    +
    +# The single grouped headers represent animation keyframe values which can be used and specify a single
    +# protobuf value for the header keyword.
    +SINGLE_HEADERS = {
    +    "body_x": body_x_handler,
    +    "body_y": body_y_handler,
    +    "body_z": body_z_handler,
    +    "com_x": com_x_handler,
    +    "com_y": com_y_handler,
    +    "com_z": com_z_handler,
    +    "body_quat_w": body_quat_w_handler,
    +    "body_quat_x": body_quat_x_handler,
    +    "body_quat_y": body_quat_y_handler,
    +    "body_quat_z": body_quat_z_handler,
    +    "body_roll": body_roll_handler,
    +    "body_pitch": body_pitch_handler,
    +    "body_yaw": body_yaw_handler,
    +    "fl_hx": fl_hx_handler,
    +    "fl_hy": fl_hy_handler,
    +    "fl_kn": fl_kn_handler,
    +    "fr_hx": fr_hx_handler,
    +    "fr_hy": fr_hy_handler,
    +    "fr_kn": fr_kn_handler,
    +    "hl_hx": hl_hx_handler,
    +    "hl_hy": hl_hy_handler,
    +    "hl_kn": hl_kn_handler,
    +    "hr_hx": hr_hx_handler,
    +    "hr_hy": hr_hy_handler,
    +    "hr_kn": hr_kn_handler,
    +    "fl_x": fl_x_handler,
    +    "fl_y": fl_y_handler,
    +    "fl_z": fl_z_handler,
    +    "fr_x": fr_x_handler,
    +    "fr_y": fr_y_handler,
    +    "fr_z": fr_z_handler,
    +    "hr_x": hr_x_handler,
    +    "hr_y": hr_y_handler,
    +    "hr_z": hr_z_handler,
    +    "hl_x": hl_x_handler,
    +    "hl_y": hl_y_handler,
    +    "hl_z": hl_z_handler,
    +    "fl_contact": fl_contact_handler,
    +    "fr_contact": fr_contact_handler,
    +    "hl_contact": hl_contact_handler,
    +    "hr_contact": hr_contact_handler,
    +    "shoulder0": sh0_handler,
    +    "shoulder1": sh1_handler,
    +    "elbow0": el0_handler,
    +    "elbow1": el1_handler,
    +    "wrist0": wr0_handler,
    +    "wrist1": wr1_handler,
    +    "hand_x": hand_x_handler,
    +    "hand_y": hand_y_handler,
    +    "hand_z": hand_z_handler,
    +    "hand_quat_w": hand_quat_w_handler,
    +    "hand_quat_x": hand_quat_x_handler,
    +    "hand_quat_y": hand_quat_y_handler,
    +    "hand_quat_z": hand_quat_z_handler,
    +    "hand_roll": hand_roll_handler,
    +    "hand_pitch": hand_pitch_handler,
    +    "hand_yaw": hand_yaw_handler,
    +    "gripper": gripper_handler,
    +    "time": start_time_handler,
    +}
    +
    +COMMENT_DELIMITERS = ["//", "#"]
    +
    +
    +
    [docs]class Animation(): + """Helper class to track values read from the animation file that are important to choreographer + and necessary when uploading animated moves.""" + + def __init__(self): + # The name of the animated move. + self.name = None + + # [Optional] The BPM which the move will be performed at. + self.bpm = None + + # [Optional] The frequency at which the keyframes occur. If not provided in the CHA file, then + # explicit timestamps must be provided for each keyframe. + self.frequency = None + + # Default description for the animated move. + self.description = "Animated dance move." + + # The protobuf message representing the animation. + self.proto = choreography_sequence_pb2.Animation() + + # The color of the animated move's block when loaded in Choreographer. + self.rgb = [100, 100, 100] + + # Each individual parameter line as read from a *.cha file. + self.parameter_lines = [] + +
    [docs] def create_move_info_proto(self): + """Creates the MoveInfo protobuf message from the parsed animation file. + + Returns: + The choreography_sequence.MoveInfo protobuf message for the animation as generated + by the different animation fields in the Animation proto. + """ + move_info = choreography_sequence_pb2.MoveInfo() + move_info.name = self.name + move_info.is_extendable = self.proto.extendable or self.proto.truncatable # "is adjustable" + + # Should always have move.time, so the duration is the final time of the last frame. + move_duration_sec = self.proto.animation_keyframes[-1].time + if self.bpm is not None: + # Compute the move length slices using the bpm. + slices_per_minute = 4 * self.bpm + move_duration_minutes = move_duration_sec / 60.0 + move_info.move_length_slices = int(move_duration_minutes * slices_per_minute) + else: + # Just use the time to size the move. + move_info.move_length_time = move_duration_sec + + # Set the max/min info based on truncatable/extendable flags. + if self.proto.truncatable and not self.proto.extendable: + move_info.max_time = move_info.move_length_time + move_info.max_move_length_slices = move_info.move_length_slices + elif self.proto.extendable and not self.proto.truncatable: + move_info.min_time = move_info.move_length_time + move_info.min_move_length_slices = move_info.move_length_slices + + # Set the different track information + move_info.controls_arm = self.proto.controls_arm + move_info.controls_gripper = self.proto.controls_gripper + move_info.controls_legs = self.proto.controls_legs + move_info.controls_body = self.proto.controls_body + + # Set the choreographer-specific display information + move_info.display.category = choreography_sequence_pb2.ChoreographerDisplayInfo.CATEGORY_ANIMATION + move_info.display.color.r = self.rgb[0] + move_info.display.color.g = self.rgb[1] + move_info.display.color.b = self.rgb[2] + move_info.display.color.a = 1.0 + move_info.display.description = self.description + + # Animations are required to start and end in a standing position. + move_info.entrance_states.append(choreography_sequence_pb2.MoveInfo.TRANSITION_STATE_STAND) + move_info.exit_state = choreography_sequence_pb2.MoveInfo.TRANSITION_STATE_STAND + + return move_info
    + + +
    [docs]def set_proto(proto_object, attribute_name, attribute_value): + """Helper function to set a field to a specific value in the protobuf message. + + Args: + proto_object (Protobuf message): Any generic protobuf message. + attribute_name (String): The field name within the protobuf message. + attribute_value: A value with type matching the field type defined in the protobuf + message definition. This will be saved in the attribute_name field. + + Returns: + Nothing. Mutates the input proto_object to update the specified field name to the + provided value. + """ + field_attr = getattr(proto_object, attribute_name) + field_type = type(field_attr.value) + field_attr.value = field_type(attribute_value)
    + + +
    [docs]def handle_nested_double_value_params(proto_object, name, attribute_value): + """Helper function to set a field to a DoubleValue protobuf in a protobuf message. + + Args: + proto_object (Protobuf message): Any generic protobuf message. + name (String): The field name within the protobuf message. This name should + be both the field name and sub-field name separated by a period. For example, + for the Vec3 velocity field, the name would be 'velocity.x'. + attribute_value: A value with type matching the field type defined in the protobuf + message definition. This will be saved in the attribute_name field. + + Returns: + Nothing. Mutates the input proto_object to update the specified field name to the + provided value. + """ + subfields = name.split(".") + current_attr = proto_object + for field in subfields: + current_attr = getattr(current_attr, field) + current_attr.CopyFrom(wrappers_pb2.DoubleValue(value=attribute_value))
    + + +
    [docs]def read_animation_params(animation): + """Parses the set of lines that are the parameters section of the file. + + Reads the parameter lines into the min/max/default values in the Animation proto. + + Args: + animation (Animation): The animation class structure containing the parameter lines. + + Returns: + The mutated animation class, which now contains populated params fields in the + animation's protobuf. + """ + current_params_default = animation.proto.default_parameters + current_params_min = animation.proto.minimum_parameters + current_params_max = animation.proto.maximum_parameters + group_field_splitter = "." + for param in animation.parameter_lines: + split_line = param.split() + param_name = str(split_line[0]) + min_max_default_vals = [ + float(split_line[i]) if float(split_line[i]) != 0 else 1e-6 for i in range(1, 4) + ] + + # Now create the parameter protobuf message. + if group_field_splitter in param_name: + # Non-individual fields, so handle slightly differently. + try: + handle_nested_double_value_params(current_params_min, param_name, + min_max_default_vals[0]) + handle_nested_double_value_params(current_params_default, param_name, + min_max_default_vals[1]) + handle_nested_double_value_params(current_params_max, param_name, + min_max_default_vals[2]) + except AttributeError: + err = "Cannot parse file %s: unknown parameter field name %s." % (animation.name, + param_name) + raise Exception(err) + else: + # Individual field using a DoubleValue proto. + try: + set_proto(current_params_min, param_name, min_max_default_vals[0]) + set_proto(current_params_default, param_name, min_max_default_vals[1]) + set_proto(current_params_max, param_name, min_max_default_vals[2]) + except AttributeError: + err = "Cannot parse file %s: unknown parameter field name %s." % (animation.name, + param_name) + raise Exception(err) + return animation
    + + +
    [docs]def read_and_find_animation_params(animate_move_params_file, filepath_input=True): + """Create a mapping of the parameter name to the default parameter values. + + Args: + animate_move_params_file (string): filepath to the default parameters file, or a string representing the contents + of the parameters file. + filepath_input (boolean): With filepath_input set to True, the animate_move_params_file argument will be interpreted as + a file path to the default parameters file. When set to False, the animate_move_params_file argument will be read as + the information in the default parameters file passed as a string. + + Returns: + A dictionary containing the parameter name as the key, and the full parameter line from + the file as the value. + """ + if(filepath_input): + #if animate_move_params_file is a filepath open the file + params_file = open(animate_move_params_file, "r") + else: + #if animate_move_params_file is a string of parameter information split the string by newline characters + params_file = animate_move_params_file.splitlines() + + reading_animate_params = False + animate_params_vals = {} + for line in params_file: + line = line.strip() + if "animate_params" in line: + # Starting the animate params section. + reading_animate_params = True + continue + + if reading_animate_params and line == "": + # Finished reading the animate_params section. + reading_animate_params = False + return animate_params_vals + + if reading_animate_params: + # Set the parameter name as the key, and the full parameter line (as a string) as the value in + # the animate_params_vals dictionary. + split_line = line.split() + param_name = split_line[0] + animate_params_vals[param_name] = line + continue + return animate_params_vals
    + + +
    [docs]def convert_animation_file_to_proto(animated_file, animate_move_params_file=""): + """Parses a file into the animation proto that will be uploaded to the robot. + + Args: + animated_file (string): The filepath to the animation text file. + animate_move_params_file (string): [Optional] The filepath to a default set of move parameters. + + Returns: + The Animation class, which contains the animation proto to be uploaded to the robot, as well + as additional information to be used by Choreographer. + """ + + # Create a mapping of the default values for each parameter from the MoveParamsConfig.txt file. + # These will be used if a user doesn't provide min/max/default, but does include the parameter + # name in the file. + maybe_use_default_params = animate_move_params_file != "" + default_animate_params_values = {} + if maybe_use_default_params: + if os.path.exists(animate_move_params_file): + # if there is a file at the location animate_move_params_file try to read the file + default_animate_params_values = read_and_find_animation_params(animate_move_params_file) + else: + # if animate_move_params_file isn't a locatable file try to read the string as parameter field data + default_animate_params_values = read_and_find_animation_params(animate_move_params_file, False) + + animation = Animation() + animation.name = ntpath.basename(animated_file).split(".cha")[0] + + animation_specs = open(animated_file, "r") + + # Expecting three chunks, separated by a blank line. + section_counter = 0 + + # The set of keywords that describe each column in the movement section. + movement_columns = [] + + # If the keyframe needs the timestamps set based off the frequency, track that information here. + # Value 1: indicates if it needs the timestamps set, value 2: indicates the current cumulative time + # summed while iterating over each keyframe in the file. + set_keyframe_times = [False, 0] + + # Make up a unique color that is persistent based on the animation name. + name_hash = hashlib.md5(animation.name.encode()) + animation.rgb[0] = int(name_hash.hexdigest()[0:2], 16) + animation.rgb[1] = int(name_hash.hexdigest()[2:4], 16) + animation.rgb[2] = int(name_hash.hexdigest()[4:6], 16) + + for line in animation_specs: + line = line.strip() + + if line == "": + # The sections are separated by an empty line + section_counter += 1 + continue + + # Check if there are any comments. Comments can be both at the end of an exisiting line, or + # on a line of there own. They are marked with # or //. We want to just ignore them and continue + # parsing the file as normal. + for delim in COMMENT_DELIMITERS: + line = line.split(delim)[0] # Take any content before the comment starts. + + if line == "": + # If after stripping all the comment content there is no line left, then continue. + # We do NOT increment the section counter for comment lines! + continue + + if section_counter == 0: + # the first section is the "options section". + # Take first word of line and use that as the options keyword. Apply whatever function + # is specified for that keyword to the remaining line values. + file_line_split = line.split() + keyword = file_line_split[0] + if keyword in OPTIONS_KEYWORDS_TO_FUNCTION: + # Apply the keywords functionality to the animation class. + OPTIONS_KEYWORDS_TO_FUNCTION[keyword](file_line_split, animation) + + elif section_counter == 1: + # Second section represents the parameters for choreographer. Simply store the lines + # for use by choreographer's config reader. + line = line.strip().lower() + if "no parameters" in line: + # This is the keyword for no parameters, so just skip the section and move on. + continue + split_line = line.split() + if len(split_line) == 1 and maybe_use_default_params: + # If the parameter name was provided with no user-specified min/max/default values, + # then attempt to use the default values from MoveParamsConfig.txt. + if split_line[0] in default_animate_params_values: + animation.parameter_lines.append(default_animate_params_values[split_line[0]]) + else: + err = "Cannot parse file %s: parameter field name %s was provided but is not a default parameter." % ( + animation.name, split_line[0]) + raise Exception(err) + else: + animation.parameter_lines.append(line) + + elif section_counter == 2: + # The final section is the animated moves section. + if len(movement_columns) == 0: + # The first line will contain all the different column headers. + movement_columns = line.split() + + # If "time" is not in the column, then we should set that for every keyframe based on frequency + if "time" not in movement_columns: + set_keyframe_times[0] = True + if animation.frequency is None: + prefix = "Cannot parse file %s: " % animation.name + err = prefix + "Either frequency or keyframe timestamps must be provided. Neither were found." + raise Exception(err) + continue + + vals = line.split() + animation_keyframe = choreography_sequence_pb2.AnimationKeyframe() + current_index = 0 + for header in movement_columns: + if header in GROUPED_HEADERS: + # For grouped headers, get the next N line values, where N is specified by the grouped + # headers dict, and set those in the animation keyframe protobuf message. + header_activities = GROUPED_HEADERS[header] + header_values = vals[current_index:current_index + + header_activities[0]] # not inclusive + header_values = [float(val) if val != '0' else 1e-6 for val in header_values] + header_activities[1](header_values, animation_keyframe) + current_index = current_index + header_activities[0] + elif header in SINGLE_HEADERS: + # Add the single value into the animation keyframe protobuf message. + header_value = float(vals[current_index]) + if header_value == 0: + header_value = 1e-6 + SINGLE_HEADERS[header](header_value, animation_keyframe) + current_index += 1 + else: + # Don't fail silently and mismatch indices of other groups if one group header is not found. + err = "Cannot parse file %s: Unknown body movement keyword %s" % ( + animation.name, header) + raise Exception(err) + + if set_keyframe_times[0]: + # Update the timestamp in the keyframe, then increment it based on the frequency + animation_keyframe.time = set_keyframe_times[1] + set_keyframe_times[1] += (1.0 / animation.frequency) + + # Add the animation frame into the animation proto. + animation.proto.animation_keyframes.extend([animation_keyframe]) + + else: + # An animation file should only have 3 sections: the options, the parameters, and the body movement keyframes. + err = ( + "Cannot parse file %s: Animation file contains more than 3 sections deliniated by whitespace." + " Make sure all comments are included in a exisiting section." % (animation.name)) + raise Exception(err) + + animation.proto.name = animation.name + if animation.bpm is not None: + animation.proto.bpm = animation.bpm + + # Read out the parameters into protobuf messages. + read_animation_params(animation) + + return animation
    + + +
    [docs]def write_animation_to_dest(animation, destination): + """Write the new animation proto to a .cap file. + + Args: + animation(Animation class): The animation class object generated by the + `cha` file conversion helpers to save the protobuf from. + destination (string): The full filepath to the location to save the animation + protobuf message. + """ + if (animation.name is None): + LOGGER.error("Invalid file name, cannot save choreography sequence.") + raise IOError("Invalid file name, cannot save choreography sequence.") + + if not os.path.exists(destination): + LOGGER.error("Path(%s) to save file does not exist. Creating it." % destination) + os.makedirs(destination, exist_ok=True) + + animation_proto_bytes = text_format.MessageToString(animation.proto) + with open(str(os.path.join(destination, animation.name + ".cap")), 'w') as f: + f.write(animation_proto_bytes)
    + + +
    [docs]def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--cha-filepath', help='The filename of the animation file.') + parser.add_argument('--cha-directory', help="The filepath to a directory with animation files.") + parser.add_argument( + '--config-filepath', help= + 'The filepath for a move params config file. This can be found using the ListAllMoves RPC.', + default="") + parser.add_argument('--destination-filepath', + help='The file location to save the animation proto.', default=".") + options = parser.parse_args() + + if options.cha_filepath: + animation = convert_animation_file_to_proto(options.cha_filepath, options.config_filepath) + write_animation_to_dest(animation, options.destination_filepath) + + elif options.cha_directory: + files_in_dir = os.listdir(options.cha_directory) + for filename in files_in_dir: + if filename.endswith(".cha"): + animation = convert_animation_file_to_proto( + os.path.join(options.cha_directory, filename), options.config_filepath) + write_animation_to_dest(animation, options.destination_filepath) + + else: + print( + "Please provide either the --cha-filepath argument for a single animation file, or the --cha-directory argument" + " for a full directory of animation files.") + + return True
    + + +if __name__ == '__main__': + if not main(): + sys.exit(1) +
    + +
    + +
    + +
    +
    + +
    + +
    + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/html/_modules/bosdyn/choreography/client/choreography.html b/docs/html/_modules/bosdyn/choreography/client/choreography.html index 303244398..b096e1262 100644 --- a/docs/html/_modules/bosdyn/choreography/client/choreography.html +++ b/docs/html/_modules/bosdyn/choreography/client/choreography.html @@ -7,7 +7,7 @@ - bosdyn.choreography.client.choreography — Spot 2.3.5 documentation + bosdyn.choreography.client.choreography — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -552,30 +593,35 @@

    Source code for bosdyn.choreography.client.choreography

    # Development Kit License (20191101-BDSDK-SL). """For clients to use the choreography service""" +import collections import logging import os -import collections +import hashlib -from bosdyn.client.common import BaseClient -from bosdyn.client.common import (error_factory, error_pair, common_header_errors, handle_common_header_errors, common_lease_errors, +from google.protobuf import text_format + +from bosdyn.api.spot import (choreography_sequence_pb2, choreography_service_pb2, + choreography_service_pb2_grpc) +from bosdyn.client.common import (BaseClient, common_header_errors, common_lease_errors, + error_factory, error_pair, handle_common_header_errors, handle_lease_use_result_errors, handle_unset_status_error) from bosdyn.client.exceptions import ResponseError, UnsetStatusError -from bosdyn.client.robot_command import NoTimeSyncError, _TimeConverter -from bosdyn.api.spot import choreography_sequence_pb2, choreography_service_pb2, choreography_service_pb2_grpc - from bosdyn.client.lease import add_lease_wallet_processors - -from google.protobuf import text_format +from bosdyn.client.robot_command import NoTimeSyncError, _TimeConverter +from bosdyn.util import seconds_to_duration +from google.protobuf.wrappers_pb2 import StringValue LOGGER = logging.getLogger('__name__') +
    [docs]class ChoreographyClient(BaseClient): """Client for Choreography Service.""" default_service_name = 'choreography' service_type = 'bosdyn.api.spot.ChoreographyService' def __init__(self): - super(ChoreographyClient, self).__init__(choreography_service_pb2_grpc.ChoreographyServiceStub) + super(ChoreographyClient, + self).__init__(choreography_service_pb2_grpc.ChoreographyServiceStub) self._timesync_endpoint = None
    [docs] def update_from(self, other): @@ -598,54 +644,398 @@

    Source code for bosdyn.choreography.client.choreography

    [docs] def list_all_moves(self, **kwargs): """Get a list of the different choreography sequence moves and associated parameters.""" req = choreography_sequence_pb2.ListAllMovesRequest() - return self.call(self._stub.ListAllMoves, req, - value_from_response=None, # Return the complete response message - error_from_response=common_header_errors, **kwargs)
    + return self.call( + self._stub.ListAllMoves, + req, + value_from_response=None, # Return the complete response message + error_from_response=common_header_errors, + **kwargs)
    [docs] def list_all_moves_async(self, object_type=None, time_start_point=None, **kwargs): """Async version of list_all_moves().""" req = choreography_sequence_pb2.ListAllMovesRequest() - return self.call_async(self._stub.ListAllMoves, req, - value_from_response=None, # Return the complete response message - error_from_response=common_header_errors, **kwargs)
    + return self.call_async( + self._stub.ListAllMoves, + req, + value_from_response=None, # Return the complete response message + error_from_response=common_header_errors, + **kwargs)
    [docs] def upload_choreography(self, choreography_seq, non_strict_parsing=True, **kwargs): - """Upload the choreography sequence to the robot.""" - req = choreography_sequence_pb2.UploadChoreographyRequest(choreography_sequence=choreography_seq, non_strict_parsing=non_strict_parsing) - return self.call(self._stub.UploadChoreography, req, - value_from_response=None, # Return the complete response message - error_from_response=common_header_errors, - **kwargs)
    + """Upload the choreography sequence to the robot. + + Args: + choreography_seq (choreography_sequence_pb2.ChoreographySequence proto): The + dance sequence to be sent and stored on the robot. + non_strict_parsing (boolean): If true, the robot will fix any correctable errors within + the choreogrpahy and allow users to run the dance. If false, if there are errors + the robot will reject the choreography when attempting to run the dance. + + Returns: + The UploadChoreographyResponse message, which includes any warnings generated from the + validation process for the choreography. If non_strict_parsing=False and there are warnings, + then the dance will not be able to run. + """ + req = choreography_sequence_pb2.UploadChoreographyRequest( + choreography_sequence=choreography_seq, non_strict_parsing=non_strict_parsing) + return self.call( + self._stub.UploadChoreography, + req, + value_from_response=None, # Return the complete response message + error_from_response=common_header_errors, + **kwargs)
    [docs] def upload_choreography_async(self, choreography_seq, non_strict_parsing=True, **kwargs): """Async version of upload_choreography().""" - req = choreography_sequence_pb2.UploadChoreographyRequest(choreography_sequence=choreography_seq, non_strict_parsing=non_strict_parsing) - return self.call_async(self._stub.UploadChoreography, req, - value_from_response=None, # Return the complete response message - error_from_response=common_header_errors, **kwargs)
    + req = choreography_sequence_pb2.UploadChoreographyRequest( + choreography_sequence=choreography_seq, non_strict_parsing=non_strict_parsing) + return self.call_async( + self._stub.UploadChoreography, + req, + value_from_response=None, # Return the complete response message + error_from_response=common_header_errors, + **kwargs)
    + +
    [docs] def upload_animated_move(self, animation, generated_id="", **kwargs): + """Upload the animation proto to the robot to be used as a move in choreography sequences. + + Args: + animation (choreography_sequence_pb2.Animation): The animated move protobuf message. This + can be generated by converting a `cha` file using the animation_file_to_proto helpers. + generated_id (string): The ID hash generated for the animation based on the serialization + of the protobuf message. This can be left empty, and the robot will re-parse and + validate the message. This will be filled out automatically when using + the AnimationUploadHelper. + + Returns: + The UploadAnimatedMoveResponse message, which includes warnings if the uploaded animation + was invalid. + """ + gen_id_proto = StringValue(value=generated_id) + req = choreography_sequence_pb2.UploadAnimatedMoveRequest( + animated_move=animation, animated_move_generated_id=gen_id_proto) + return self.call( + self._stub.UploadAnimatedMove, + req, + value_from_response=None, # Return the complete response message + error_from_response=_upload_animated_move_errors, + **kwargs)
    + +
    [docs] def upload_animated_move_async(self, animation, generated_id="", **kwargs): + """Async version of upload_animated_move().""" + gen_id_proto = StringValue(value=generated_id) + req = choreography_sequence_pb2.UploadAnimatedMoveRequest( + animated_move=animation, animated_move_generated_id=gen_id_proto) + return self.call_async( + self._stub.UploadAnimatedMove, + req, + value_from_response=None, # Return the complete response message + error_from_response=_upload_animated_move_errors, + **kwargs)
    + + +
    [docs] def choreography_log_to_animation_file(self, name, fpath, has_arm, *args): + """Turn the choreography log from the proto into an animation `cha` file type. + + Args: + name (string): Name that the `cha` file will be saved as. + fpath (string): Location where the new `cha` file will be saved. + has_arm (boolean): True if the robot has an arm, False if the robot doesn't have an arm. When False arm motion won't + be added to the `cha` file. + *args (string(s) or list): String(s), list of strings, or a mix of string(s) and list(s) that are the options to be + included in the `cha` file. (ex. 'truncatable') + + Returns: + The filename of the new animation `cha` file for the most recent choreography log. + """ + + def option_list(*argvs): + """Takes a list of options or a variable number of string arguments, or a mix of the two and returns a single list + containing all of the options to be included + """ + x=[] + for i in argvs: + if 'list' in str(type(i)): + x+=list(i) + elif 'str' in str(type(i)): + x.append(i) + return x + + def list_to_formated_string(alist, space): + """Helper function for formatting the `cha` file columns.""" + format_str = ''.join(space for _ in alist) + return format_str.format(*alist) + + def timestamp_to_seconds(timestamp): + """Helper function to turn a seconds quantity and nanoseconds quantity into one value of unit seconds.""" + return timestamp.seconds + 1e-9*timestamp.nanos + + + #get the choreography log for the recording + log_type = choreography_sequence_pb2.DownloadRobotStateLogRequest.LOG_TYPE_MANUAL + response_status, choreography_log = self.download_robot_state_log(log_type) + + #create the header for the *.cha file + joint_type_list = ["leg_joints", "body_pos", "body_quat_xyzw", "time", "contact"] + controls_header = "controls legs body" + description = "Animation created from log recording." + + if has_arm: + #if the robot has an arm, add the gripper and arm to the header description and keywords section + joint_type_list.append("arm_joints") + joint_type_list.append("gripper") + controls_header = controls_header + " arm gripper" + + #format the complete header with all the options to be included in the *.cha file + joint_spacer = "{:<60}" + header = (controls_header + "\n" + "description: " + description + "\n") + + for option in option_list(*args): + header = header + option + "\n" + + header = header + ("\n" + "no parameters\n\n" + list_to_formated_string(joint_type_list, joint_spacer) + "\n") + + spacer_val = '{:<30}' + ext=".cha" + file_path = os.path.join(fpath, name + ext) + initial_time = -1 + + with open(file_path, 'w') as f: + #write the formatted header to the *.cha file + f.write(header) + + #create an empty list to hold the values for the current keyframe + list_values=[] + + # get the list of all the keyframes from the recording, + # (the complete description of the robot in space at each timestamp) + keyframe_list = choreography_log.key_frames + + for k in keyframe_list: + # for each animation keyframe in the protobuf log put the desired joint values and timestamp in a list + # and then write them to the *.cha file. Values are added to list_values in the same order as the + # joint categories in joint_type_list, and the order of the joint angles within those groups must also + # go in the correct order for the *.cha to be read correctly. + list_values=[] + + #leg_joints values: + #front right leg joint values + list_values.append(k.joint_angles.fr.hip_x) + list_values.append(k.joint_angles.fr.hip_y) + list_values.append(k.joint_angles.fr.knee) + + #front left leg joint values + list_values.append(k.joint_angles.fl.hip_x) + list_values.append(k.joint_angles.fl.hip_y) + list_values.append(k.joint_angles.fl.knee) + + #hind right leg joint values + list_values.append(k.joint_angles.hr.hip_x) + list_values.append(k.joint_angles.hr.hip_y) + list_values.append(k.joint_angles.hr.knee) + + #hind left leg joint values + list_values.append(k.joint_angles.hl.hip_x) + list_values.append(k.joint_angles.hl.hip_y) + list_values.append(k.joint_angles.hl.knee) + + #body_pos values: + #position of the body in the animation frame + list_values.append(k.animation_tform_body.position.x) + list_values.append(k.animation_tform_body.position.y) + list_values.append(k.animation_tform_body.position.z) + + #body_quat_xyzw values: + #rotation of the body in the animation frame + list_values.append(k.animation_tform_body.rotation.x) + list_values.append(k.animation_tform_body.rotation.y) + list_values.append(k.animation_tform_body.rotation.z) + list_values.append(k.animation_tform_body.rotation.w) + + #time value: + #time, in seconds, when the keyframe position occurs relative to the start of the recording + time = timestamp_to_seconds(k.timestamp) + + #if the initial time is negative it's the first timestamp recorded, set that as the initial time + if(initial_time < 0): + initial_time = time + + #adjust the timestamp so it's relative to the start of the recording + time = time - initial_time + list_values.append(time) + + #contact values: + #contact state of each foot; 0 for airborne, 1 for contact with the floor. + list_values.append(k.foot_contact_state.fr_contact) + list_values.append(k.foot_contact_state.fl_contact) + list_values.append(k.foot_contact_state.hr_contact) + list_values.append(k.foot_contact_state.hl_contact) + + if has_arm: + #if the robot has an arm, record the joint values of the arm and gripper + + #arm_joints values: + list_values.append(k.joint_angles.arm.shoulder_0.value) + list_values.append(k.joint_angles.arm.shoulder_1.value) + list_values.append(k.joint_angles.arm.elbow_0.value) + list_values.append(k.joint_angles.arm.elbow_1.value) + list_values.append(k.joint_angles.arm.wrist_0.value) + list_values.append(k.joint_angles.arm.wrist_1.value) + + #gripper value: + list_values.append(k.joint_angles.gripper_angle.value) + + #format the values of the keyframe and write them to the *.cha animation file + f.write(list_to_formated_string(list_values, spacer_val)) + f.write("\n") + + print("Animation *.cha file downloaded to: %s" % file_path) + return name + ".cha"
    + + +
    [docs] def execute_choreography(self, choreography_name, client_start_time, + choreography_starting_slice, lease=None, **kwargs): + """Execute the current choreography sequence loaded on the robot by name. + + Args: + choreography_name (string): The name of the uploaded choreography to run. The robot + only stores a single choreography at a time, so this name should match the last + uploaded choreography. + client_start_time (float): The time (in seconds) that the dance should start. This time + should be provided in the local clock's timeframe and the client will convert it + to the required robot's clock timeframe. + choreography_starting_slice (int): Which slice to start the dance at when the start + time is reached. By default, it will start with the first slice. + lease (lease_pb2.Lease protobuf): A specific lease to use for the request. If nothing is + provided, the client will append the next lease sequence in this field by default. + + Returns: + The full ExecuteChoreographyResponse message. + """ + req = self.build_execute_choreography_request(choreography_name, client_start_time, + choreography_starting_slice, lease) + + return self.call( + self._stub.ExecuteChoreography, + req, + value_from_response=None, # Return the complete response message + error_from_response=_execute_choreography_errors, + **kwargs)
    + +
    [docs] def execute_choreography_async(self, choreography_name, client_start_time, + choreography_starting_slice, lease=None, **kwargs): + """Async version of execute_choreography().""" + req = self.build_execute_choreography_request(choreography_name, client_start_time, + choreography_starting_slice, lease) + return self.call_async( + self._stub.ExecuteChoreography, + req, + value_from_response=None, # Return the complete response message + error_from_response=_execute_choreography_errors, + **kwargs)
    + +
    [docs] def start_recording_state(self, duration_secs, continue_session_id=0, **kwargs): + """Start (or continue) a manually recorded robot state log. + + Args: + duration_secs (float): The duration of the recording request in seconds. This will + apply from when the StartRecording rpc is recieved. + continue_session_id (int): If provided, the RPC will continue the recording + session associated with that ID. + + Returns: + The full StartRecordingStateResponse proto. + """ + request = self.build_start_recording_state_request(duration_secs, continue_session_id) + return self.call( + self._stub.StartRecordingState, + request, + value_from_response=None, # Return the complete response message + error_from_response=_start_recording_state_errors, + **kwargs)
    + +
    [docs] def start_recording_state_async(self, duration_secs, continue_session_id=0, **kwargs): + """Async version of start_recording_state().""" + request = self.build_start_recording_state_request(duration_secs, continue_session_id) + return self.call_async( + self._stub.StartRecordingState, + request, + value_from_response=None, # Return the complete response message + error_from_response=_start_recording_state_errors, + **kwargs)
    + +
    [docs] def stop_recording_state(self, **kwargs): + """Stop recording a manual choreography log. + + Returns: + The full StopRecordingStateResponse proto. + """ + request = choreography_sequence_pb2.StopRecordingStateRequest() + return self.call( + self._stub.StopRecordingState, + request, + value_from_response=None, # Return the complete response message + error_from_response=common_header_errors, + **kwargs)
    + +
    [docs] def stop_recording_state_async(self, **kwargs): + """Async version of stop_recording_state().""" + request = choreography_sequence_pb2.StopRecordingStateRequest() + return self.call_async( + self._stub.StopRecordingState, + request, + value_from_response=None, # Return the complete response message + error_from_response=common_header_errors, + **kwargs)
    + +
    [docs] @staticmethod + def build_start_recording_state_request(duration_seconds=None, continue_session_id=0): + """Generate a StartRecordingStateRequest proto. + + Args: + duration_seconds (float): The duration of the recording request in seconds. This will + apply from when the StartRecording rpc is recieved. + continue_session_id (int): If provided, the RPC will continue the recording + session associated with that ID. + + Returns: + The full StartRecordingStateRequest proto with fields populated based on the input arguments. + """ + request = choreography_sequence_pb2.StartRecordingStateRequest() + request.recording_session_id = continue_session_id + if duration_seconds is not None: + request.continue_recording_duration.CopyFrom(seconds_to_duration(duration_seconds)) + return request
    -
    [docs] def execute_choreography(self, choreography_name, client_start_time, choreography_starting_slice, lease=None, **kwargs): - """Execute the current choreography sequence loaded on the robot by name.""" - req = self.build_execute_choreography_request(choreography_name, client_start_time, choreography_starting_slice, lease) - return self.call(self._stub.ExecuteChoreography, req, - value_from_response=None, # Return the complete response message - error_from_response=_execute_choreography_errors, - **kwargs)
    +
    [docs] def download_robot_state_log(self, log_type, **kwargs): + """Download the manual or automatically collected logs for choreography robot state. -
    [docs] def execute_choreography_async(self, choreography_name, client_start_time, choreography_starting_slice, lease=None, **kwargs): - """Async version of execute_choreography().""" - req = self.build_execute_choreography_request(choreography_name, client_start_time, choreography_starting_slice, lease) - return self.call_async(self._stub.ExecuteChoreography, req, - value_from_response=None, # Return the complete response message - error_from_response=_execute_choreography_errors, **kwargs)
    + Args: + log_type(choreography_sequence_pb2.LogType): Type of log, either manual or the automatically + generated log for the latest choreography. -
    [docs] def build_execute_choreography_request(self, choreography_name, client_start_time, choreography_starting_slice, lease=None): + Returns: + A tuple containing the response status (choreography_sequence_pb2.DownloadRobotStateLogResponse.Status) and + the choreography_sequence_pb2.ChoreographyStateLog constructed from the streaming response message. + """ + request = choreography_sequence_pb2.DownloadRobotStateLogRequest(log_type=log_type) + return self.call( + self._stub.DownloadRobotStateLog, + request, + value_from_response=_get_streamed_choreography_state_log, # Parses streamed response + error_from_response=_download_robot_state_log_stream_errors, + **kwargs)
    + +
    [docs] def build_execute_choreography_request(self, choreography_name, client_start_time, + choreography_starting_slice, lease=None): """Generate the ExecuteChoreographyRequest rpc with the timestamp converted into robot time.""" # Note the client_start_time is a time expressed in the client's clock for when the choreography sequence should begin. - request = choreography_sequence_pb2.ExecuteChoreographyRequest(choreography_sequence_name=choreography_name, - choreography_starting_slice=float(choreography_starting_slice), - lease=lease) + request = choreography_sequence_pb2.ExecuteChoreographyRequest( + choreography_sequence_name=choreography_name, + choreography_starting_slice=float(choreography_starting_slice), lease=lease) if client_start_time is not None: request.start_time.CopyFrom( self._update_timestamp_filter(client_start_time, self.timesync_endpoint)) @@ -660,39 +1050,245 @@

    Source code for bosdyn.choreography.client.choreography

    return converter.robot_timestamp_from_local_secs(timestamp)
    +
    [docs]class AnimationUploadHelper: + """Helper class to reduce reuploading animations to a robot multiple times. + + This class will generate a hash (unique ID built from the animation protobuf + message's contents) for each animation proto, and include this hash when initially + uploading animations. It will track the animations sent to the robot and the hashes, and + only sends RPCs to upload an animation when the incoming animation proto is different + from the one on robot. + + It initializes the set of known animations on robot already by using the ListAllMoves + RPC and reading the existing animation names and hashes. + + The hash function is generated using a library which guarantees consistency, even when + restarting the program. As well, the hash is built from the serialized protobuf, and + proto guarantees that within the language that the serialized message will be consistent. + + Args: + robot (Robot sdk instance): The robot to upload animations to. + """ + + ANIMATION_MOVE_PREFIX = "animation::" + + def __init__(self, robot): + self.robot = robot + self.choreography_client = robot.ensure_client(ChoreographyClient.default_service_name) + + # Track animation name and current hash on robot. + self.animation_name_to_generated_id = {} + + # Initialize the list of known animations and their hashes based on the robot's + # ListAllMoves RPC response. + self.initialize() + +
    [docs] def initialize(self): + """Determine which animations are already uploaded on robot.""" + # Get a list of all the exisiting animations on robot. + initial_move_list = self.choreography_client.list_all_moves() + + # Iterate over the list of moves the robot currently has. Track any animation moves + # by name and current animation hash. Any moves uploaded using this helper class will + # save the animation's hash in this dictionary and compare new animation hashes to + # determine + for move in initial_move_list.moves: + if AnimationUploadHelper.ANIMATION_MOVE_PREFIX in move.name: + # Use the move name without the prefix so that subsequent attempts to upload + # that move will still match correctly. + move_name = move.name.split(AnimationUploadHelper.ANIMATION_MOVE_PREFIX)[1] + gen_id = move.animated_move_generated_id.value + self.animation_name_to_generated_id[move_name] = gen_id
    + + +
    [docs] def upload_animated_move(self, animation, **kwargs): + """Uploads the animation to robot if the animation protobuf has changed. + + This will only send an UploadAnimatedMove RPC if the incoming animation + has a new hash that differs from the current hash for this animation on robot, which + indicates that the animation protobuf has changed since the last one uploaded to robot. + + Args: + animation_proto(choreography_sequence_pb2.Animation): Animation to maybe upload. + + Returns: + The UploadAnimateMoveResponse protobuf message if the animation is actually sent. + If the animation protobuf has not changed and is not sent to the robot, then this + function returns None. + """ + generated_id = self.generate_animation_id(animation) + if animation.name in self.animation_name_to_generated_id: + gen_id_on_robot = self.animation_name_to_generated_id[animation.name] + if gen_id_on_robot == generated_id: + # Exit early without uploading the animation since it already exists on robot. + return None + result = self.choreography_client.upload_animated_move(animation, generated_id, **kwargs) + if result.status == choreography_sequence_pb2.UploadAnimatedMoveResponse.STATUS_OK: + # Add the move name to the tracked list. + self.animation_name_to_generated_id[animation.name] = generated_id + return result
    + + +
    [docs] def generate_animation_id(self, animation_proto): + """Serialize an Animation protobuf message and create a hash from the binary string. + + NOTE: Protobuf's serialization will not be consistent across protobuf versions or + even just different languages serializing the same protobuf message. This means that for a + single protobuf message, there could be multiple different serializations. This is ok for the + use-case of the AnimationUploadHelper since the id's are only used for a specific + "session" of Choreographer and the robot's boot session. These are not meant to be the same + for forever and ever due to the potential inconsistencies mentioned, and should not be used + with that expectation. + + Further, if a single animation proto does not generate the same ID for one "session", then + it will just be reuploaded and processed by the robot again. + + Args: + animation_proto(choreography_sequence_pb2.Animation): Animation to generate a hash for. + + Returns: + A string representing a unique hash built from the animation proto. + """ + return hashlib.sha1(animation_proto.SerializeToString()).hexdigest()
    + +
    [docs]class InvalidUploadedChoreographyError(ResponseError): """The uploaded choreography is invalid and unable to be performed."""
    +
    [docs]class RobotCommandIssuesError(ResponseError): """A problem occurred when issuing the robot command containing the dance."""
    +
    [docs]class LeaseError(ResponseError): """Incorrect or invalid leases for data acquisition. Check the lease use results."""
    + +
    [docs]class AnimationValidationFailedError(ResponseError): + """The uploaded animation file is invalid and cannot be used in choreography sequences."""
    + + +
    [docs]class NoRecordedInformation(ResponseError): + """The choreography service has no logged robot state data."""
    + + +
    [docs]class UnknownRecordingSessionId(ResponseError): + """The recording request contains an unknown recording session ID."""
    + + +
    [docs]class RecordingBufferFull(ResponseError): + """The recording buffer is full and the current manual log will be truncated."""
    + + +
    [docs]class IncompleteData(ResponseError): + """The recording buffer filled up, the returned log will be truncated."""
    + + _EXECUTE_CHOREOGRAPHY_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None)) _EXECUTE_CHOREOGRAPHY_STATUS_TO_ERROR.update({ choreography_sequence_pb2.ExecuteChoreographyResponse.STATUS_OK: (None, None), - choreography_sequence_pb2.ExecuteChoreographyResponse.STATUS_INVALID_UPLOADED_CHOREOGRAPHY: (InvalidUploadedChoreographyError, - InvalidUploadedChoreographyError.__doc__), + choreography_sequence_pb2.ExecuteChoreographyResponse.STATUS_INVALID_UPLOADED_CHOREOGRAPHY: + (InvalidUploadedChoreographyError, InvalidUploadedChoreographyError.__doc__), choreography_sequence_pb2.ExecuteChoreographyResponse.STATUS_ROBOT_COMMAND_ISSUES: - (RobotCommandIssuesError, RobotCommandIssuesError.__doc__), + (RobotCommandIssuesError, RobotCommandIssuesError.__doc__), choreography_sequence_pb2.ExecuteChoreographyResponse.STATUS_LEASE_ERROR: (LeaseError, LeaseError.__doc__), }) + @handle_common_header_errors @handle_lease_use_result_errors @handle_unset_status_error(unset='STATUS_UNKNOWN') def _execute_choreography_errors(response): """Return an exception based on response from ExecuteChoreography RPC, None if no error.""" - return error_factory(response, response.status, - status_to_string=choreography_sequence_pb2.ExecuteChoreographyResponse.Status.Name, - status_to_error=_EXECUTE_CHOREOGRAPHY_STATUS_TO_ERROR) + return error_factory( + response, response.status, + status_to_string=choreography_sequence_pb2.ExecuteChoreographyResponse.Status.Name, + status_to_error=_EXECUTE_CHOREOGRAPHY_STATUS_TO_ERROR) + + +_UPLOAD_ANIMATED_MOVE_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None)) +_UPLOAD_ANIMATED_MOVE_STATUS_TO_ERROR.update({ + choreography_sequence_pb2.UploadAnimatedMoveResponse.STATUS_OK: (None, None), + choreography_sequence_pb2.UploadAnimatedMoveResponse.STATUS_ANIMATION_VALIDATION_FAILED: + (AnimationValidationFailedError, AnimationValidationFailedError.__doc__), +}) + + +@handle_common_header_errors +@handle_unset_status_error(unset='STATUS_UNKNOWN') +def _upload_animated_move_errors(response): + """Return an exception based on response from UploadAnimatedMove RPC, None if no error.""" + return error_factory( + response, response.status, + status_to_string=choreography_sequence_pb2.UploadAnimatedMoveResponse.Status.Name, + status_to_error=_UPLOAD_ANIMATED_MOVE_STATUS_TO_ERROR) + + +_START_RECORDING_STATE_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None)) +_START_RECORDING_STATE_STATUS_TO_ERROR.update({ + choreography_sequence_pb2.StartRecordingStateResponse.STATUS_OK: (None, None), + choreography_sequence_pb2.StartRecordingStateResponse.STATUS_UNKNOWN_RECORDING_SESSION_ID: + (UnknownRecordingSessionId, UnknownRecordingSessionId.__doc__), + choreography_sequence_pb2.StartRecordingStateResponse.STATUS_RECORDING_BUFFER_FULL: + (RecordingBufferFull, RecordingBufferFull.__doc__), +}) + + +@handle_common_header_errors +@handle_unset_status_error(unset='STATUS_UNKNOWN') +def _start_recording_state_errors(response): + """Return an exception based on response from StartRecordingState RPC, None if no error.""" + return error_factory( + response, response.status, + status_to_string=choreography_sequence_pb2.StartRecordingStateResponse.Status.Name, + status_to_error=_START_RECORDING_STATE_STATUS_TO_ERROR) + + +@handle_common_header_errors +@handle_unset_status_error(unset='STATUS_UNKNOWN') +def _download_robot_state_log_stream_errors(response): + """Return a custom exception based on download robot state log streaming response, None if no error.""" + # Iterate through the response since the download request responds with a stream. + for resp in response: + # Handle error statuses from the request. + if (resp.status == choreography_sequence_pb2.DownloadRobotStateLogResponse. + STATUS_NO_RECORDED_INFORMATION): + return NoRecordedInformation(response=resp, error_message=NoRecordedInformation.__doc__) + # All responses (in the iterator) had status_ok + return None + ''' Static helper methods. ''' +def _get_streamed_choreography_state_log(response): + """Reads a streamed response to recreate a ChoreographyStateLog proto. + + Args: + response(choreography_sequence_pb2.DownloadRobotStateLogResponse): Streamed response with the + choreography log. + + Returns: + A tuple containing the response status (choreography_sequence_pb2.DownloadRobotStateLogResponse.Status) and + the choreography_sequence_pb2.ChoreographyStateLog constructed from the streaming response message. + """ + data = '' + num_chunks = 0 + initial_status = None + for resp in response: + if num_chunks == 0: + initial_status = resp.status + data = resp.chunk.data + else: + data += resp.chunk.data + num_chunks += 1 + choreography_log = choreography_sequence_pb2.ChoreographyStateLog() + if (num_chunks > 0): + choreography_log.ParseFromString(data) + return (initial_status, choreography_log) +
    [docs]def load_choreography_sequence_from_binary_file(file_path): """Read a choreography sequence file into a protobuf ChoreographySequence message.""" if not os.path.exists(file_path): @@ -706,6 +1302,7 @@

    Source code for bosdyn.choreography.client.choreography

    return choreography_sequence
    +
    [docs]def load_choreography_sequence_from_txt_file(file_path): if not os.path.exists(file_path): LOGGER.error("File not found at: %s" % file_path) @@ -718,6 +1315,7 @@

    Source code for bosdyn.choreography.client.choreography

    return choreography_sequence
    +
    [docs]def save_choreography_sequence_to_file(file_path, file_name, choreography): """Saves a choreography sequence to a file.""" if (file_name is None or len(file_name) == 0): diff --git a/docs/html/_modules/bosdyn/client/arm_surface_contact.html b/docs/html/_modules/bosdyn/client/arm_surface_contact.html index f3f4ea989..8d286c7f8 100644 --- a/docs/html/_modules/bosdyn/client/arm_surface_contact.html +++ b/docs/html/_modules/bosdyn/client/arm_surface_contact.html @@ -7,7 +7,7 @@ - bosdyn.client.arm_surface_contact — Spot 2.3.5 documentation + bosdyn.client.arm_surface_contact — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -559,6 +600,7 @@

    Source code for bosdyn.client.arm_surface_contact

    from .lease import add_lease_wallet_processors from bosdyn.client.robot_command import NoTimeSyncError, _TimeConverter, _edit_proto +
    [docs]class ArmSurfaceContactClient(BaseClient): """Client for the ArmSurfaceContact service.""" default_service_name = 'arm-surface-contact' @@ -623,6 +665,7 @@

    Source code for bosdyn.client.arm_surface_contact

    self._update_command_timestamps(request) return self.call_async(self._stub.ArmSurfaceContact, request, **kwargs)
    + # Tree of proto fields leading to Timestamp protos which need to be converted from # client clock to robot clock values using timesync information from the robot. # Note, the "@" sign indicates a oneof field. The "None" indicates the field which @@ -632,7 +675,7 @@

    Source code for bosdyn.client.arm_surface_contact

    'pose_trajectory_in_task': { 'reference_time': None }, - 'gripper_command':{ + 'gripper_command': { 'trajectory': { 'reference_time': None } diff --git a/docs/html/_modules/bosdyn/client/async_tasks.html b/docs/html/_modules/bosdyn/client/async_tasks.html index b996954ef..5bedd3b48 100644 --- a/docs/html/_modules/bosdyn/client/async_tasks.html +++ b/docs/html/_modules/bosdyn/client/async_tasks.html @@ -7,7 +7,7 @@ - bosdyn.client.async_tasks — Spot 2.3.5 documentation + bosdyn.client.async_tasks — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/client/auth.html b/docs/html/_modules/bosdyn/client/auth.html index 839014fa8..814e3543c 100644 --- a/docs/html/_modules/bosdyn/client/auth.html +++ b/docs/html/_modules/bosdyn/client/auth.html @@ -7,7 +7,7 @@ - bosdyn.client.auth — Spot 2.3.5 documentation + bosdyn.client.auth — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -598,16 +639,16 @@

    Source code for bosdyn.client.auth

     _STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
     _STATUS_TO_ERROR.update({
         auth_pb2.GetAuthTokenResponse.STATUS_OK: (None, None),
    -    auth_pb2.GetAuthTokenResponse.STATUS_INVALID_LOGIN: (InvalidLoginError,
    -                                                         InvalidLoginError.__doc__),
    -    auth_pb2.GetAuthTokenResponse.STATUS_INVALID_TOKEN: (InvalidTokenError,
    -                                                         InvalidTokenError.__doc__),
    +    auth_pb2.GetAuthTokenResponse.STATUS_INVALID_LOGIN:
    +        (InvalidLoginError, InvalidLoginError.__doc__),
    +    auth_pb2.GetAuthTokenResponse.STATUS_INVALID_TOKEN:
    +        (InvalidTokenError, InvalidTokenError.__doc__),
         auth_pb2.GetAuthTokenResponse.STATUS_TEMPORARILY_LOCKED_OUT:
    -    (TemporarilyLockedOutError, TemporarilyLockedOutError.__doc__),
    +        (TemporarilyLockedOutError, TemporarilyLockedOutError.__doc__),
         auth_pb2.GetAuthTokenResponse.STATUS_INVALID_APPLICATION_TOKEN:
    -    (InvalidApplicationTokenError, InvalidApplicationTokenError.__doc__),
    +        (InvalidApplicationTokenError, InvalidApplicationTokenError.__doc__),
         auth_pb2.GetAuthTokenResponse.STATUS_EXPIRED_APPLICATION_TOKEN:
    -    (ExpiredApplicationTokenError, ExpiredApplicationTokenError.__doc__)
    +        (ExpiredApplicationTokenError, ExpiredApplicationTokenError.__doc__)
     })
     
     
    diff --git a/docs/html/_modules/bosdyn/client/auto_return.html b/docs/html/_modules/bosdyn/client/auto_return.html
    new file mode 100644
    index 000000000..bb4342c30
    --- /dev/null
    +++ b/docs/html/_modules/bosdyn/client/auto_return.html
    @@ -0,0 +1,754 @@
    +
    +
    +
    +
    +
    +  
    +  
    +  
    +  
    +  bosdyn.client.auto_return — Spot 3.0.0 documentation
    +  
    +
    +  
    +  
    +  
    +
    +  
    +  
    +    
    +  
    +  
    +  
    +    
    +  
    +
    +  
    +  
    +  
    +    
    +      
    +        
    +        
    +        
    +        
    +    
    +    
    +
    +    
    +    
    +     
    +
    +
    +
    +
    +   
    +  
    + + + +
    + + + + + +
    + +
    + + + + + + + + + + + + + + + + + +
    + +
      + +
    • »
    • + +
    • Module code »
    • + +
    • bosdyn.client.auto_return
    • + + +
    • + +
    • + +
    + + +
    +
    +
    +
    + +

    Source code for bosdyn.client.auto_return

    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""Client implementation of the AutoReturn service."""
    +
    +from __future__ import print_function
    +
    +import collections
    +
    +from bosdyn.client.exceptions import ResponseError
    +from bosdyn.client.common import (error_factory, handle_common_header_errors,
    +                                  handle_unset_status_error, error_pair, BaseClient)
    +from bosdyn.api.auto_return import auto_return_pb2
    +from bosdyn.api.auto_return import auto_return_service_pb2_grpc
    +
    +
    +
    [docs]class AutoReturnResponseError(ResponseError): + """Error in Auto Return RPC"""
    + + +
    [docs]class InvalidParameterError(AutoReturnResponseError): + """One or more parameters were invalid."""
    + + +
    [docs]class AutoReturnClient(BaseClient): + """A client for configuring automatic AutoReturn behavior.""" + + default_service_name = 'auto-return' + service_type = 'bosdyn.api.auto_return.AutoReturnService' + + def __init__(self): + super(AutoReturnClient, self).__init__(auto_return_service_pb2_grpc.AutoReturnServiceStub) + self._timesync_endpoint = None + +
    [docs] def configure(self, params, leases, **kwargs): + """Set the configuration of the AutoReturn system. + + Args: + params (bosdyn.api.auto_return.auto_return_pb2.Params): Parameters to use. + leases (list of bosdyn.client.Lease) + + Raises: + AutoReturnResponseError: An invalid request was received by the service. + RpcError: Problem communicating with the service. + + Returns: + The bosdyn.api.auto_return_pb2.ConfigureResponse. + """ + + request = self._configure_request(params, leases) + return self.call(self._stub.Configure, request, None, configure_error, **kwargs)
    + +
    [docs] def configure_async(self, params, leases, **kwargs): + """Async version of the configure() RPC.""" + request = self._configure_request(params, leases) + return self.call(self._stub.Configure, request, None, configure_error, **kwargs)
    + +
    [docs] def get_configuration(self, **kwargs): + """Get the configuration of the AutoReturn system. + + Raises: + RpcError: Problem communicating with the service. + + Returns: + The bosdyn.api.auto_return_pb2.GetConfigurationResponse. + """ + request = auto_return_pb2.GetConfigurationRequest() + return self.call(self._stub.GetConfiguration, request, None, None, **kwargs)
    + +
    [docs] def get_configuration_async(self, **kwargs): + """Async version of the get_configuration() RPC.""" + request = auto_return_pb2.GetConfigurationRequest() + return self.call_async(self._stub.GetConfiguration, request, None, None, **kwargs)
    + +
    [docs] def start(self, **kwargs): + """Start AutoReturn now. + Raises: + RpcError: Problem communicating with the service. + + Returns: + The bosdyn.api.auto_return_pb2.GetConfigurationResponse. + """ + request = auto_return_pb2.StartRequest() + return self.call(self._stub.Start, request, None, None, **kwargs)
    + +
    [docs] def start_async(self, **kwargs): + """Async version of the start() RPC.""" + request = auto_return_pb2.StartRequest() + return self.call_async(self._stub.Start, request, None, None, **kwargs)
    + + def _configure_request(self, params, leases): + request = auto_return_pb2.ConfigureRequest(params=params) + for lease in leases: + request.leases.add().CopyFrom(lease.lease_proto) + return request
    + + +_CONFIGURE_STATUS_TO_ERROR = collections.defaultdict(lambda: (None, None)) +_CONFIGURE_STATUS_TO_ERROR.update( + {auto_return_pb2.ConfigureResponse.STATUS_INVALID_PARAMS: error_pair(InvalidParameterError)}) + + +
    [docs]@handle_common_header_errors +@handle_unset_status_error(unset='STATUS_UNKNOWN') +def configure_error(response): + """Return a custom exception based on the Configure response, None if no error.""" + return error_factory(response, response.status, + status_to_string=auto_return_pb2.ConfigureResponse.Status.Name, + status_to_error=_CONFIGURE_STATUS_TO_ERROR)
    +
    + +
    + +
    + +
    +
    + +
    + +
    + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/html/_modules/bosdyn/client/bddf_download.html b/docs/html/_modules/bosdyn/client/bddf_download.html index 92d85b7fa..7b8621ff3 100644 --- a/docs/html/_modules/bosdyn/client/bddf_download.html +++ b/docs/html/_modules/bosdyn/client/bddf_download.html @@ -7,7 +7,7 @@ - bosdyn.client.bddf_download — Spot 2.3.5 documentation + bosdyn.client.bddf_download — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -554,10 +595,11 @@

    Source code for bosdyn.client.bddf_download

     """Code for downloading robot data in bddf format."""
     import logging
     import re
    +import ssl
     import sys
     
    -import requests
    -from urllib3.exceptions import InsecureRequestWarning
    +from urllib.request import Request, urlopen
    +from urllib.parse import urlencode
     
     from bosdyn.client.time_sync import (TimeSyncEndpoint, TimeSyncClient, NotEstablishedError,
                                          robot_time_range_from_nanoseconds, timespec_to_robot_timespan)
    @@ -566,6 +608,7 @@ 

    Source code for bosdyn.client.bddf_download

     LOGGER = logging.getLogger()
     
     REQUEST_CHUNK_SIZE = 10 * (1024**2)  # This value is not guaranteed.
    +REQUEST_TIMEOUT = 20  # Seconds.
     
     DEFAULT_OUTPUT = "./download.bddf"
     
    @@ -630,11 +673,6 @@ 

    Source code for bosdyn.client.bddf_download

         Returns:
           output filename, or None on error
         """
    -    # pylint: disable=no-member
    -    # Suppress only the single warning from urllib3 needed.
    -    requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
    -    # pylint: enable=no-member
    -
         time_sync_endpoint = None
         if not robot_time:
             # Establish time sync with robot to obtain skew.
    @@ -660,16 +698,20 @@ 

    Source code for bosdyn.client.bddf_download

             get_params['grpc_service'] = grpc_service
     
         # Request the data.
    -    url = _bddf_url(hostname)
    -    with requests.get(url, headers=_http_headers(robot), verify=False, stream=True,
    -                      params=get_params) as resp:
    -        if resp.status_code != 200:
    -            LOGGER.error("%s %s response: %d", url, get_params, resp.status_code)
    +    url = _bddf_url(hostname) + '?{}'.format(urlencode(get_params))
    +    request = Request(url, headers=_http_headers(robot))
    +    context = ssl._create_unverified_context()  # pylint: disable=protected-access
    +    with urlopen(request, context=context, timeout=REQUEST_TIMEOUT) as resp:
    +        if resp.status != 200:
    +            LOGGER.error("%s %s response: %d", url, get_params, resp.status)
                 return None
     
             outfile = output_filename if output_filename else _output_filename(resp)
             with open(outfile, 'wb') as fid:
    -            for chunk in resp.iter_content(chunk_size=REQUEST_CHUNK_SIZE):
    +            while True:
    +                chunk = resp.read(REQUEST_CHUNK_SIZE)
    +                if len(chunk) == 0:
    +                    break
                     if show_progress:
                         print('.', end='', flush=True)
                     fid.write(chunk)
    @@ -712,8 +754,8 @@ 

    Source code for bosdyn.client.bddf_download

         add_common_arguments(parser)
         options = parser.parse_args()
     
    -    if options.verbose:
    -        LOGGER.setLevel(logging.DEBUG)
    +    options.verbose = level = logging.DEBUG if options.verbose else logging.INFO
    +    logging.basicConfig(level=level, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
     
         if options.help_timespan:
             _print_help_timespan()
    @@ -731,7 +773,7 @@ 

    Source code for bosdyn.client.bddf_download

             LOGGER.error("Cannot authenticate to robot to obtain token: %s", err)
             return 1
     
    -    output_filename = download_data(robot, options.hostname, options.timespan,
    +    output_filename = download_data(robot, options.hostname, timespan_spec=options.timespan,
                                         robot_time=options.robot_time, channel=options.channel,
                                         message_type=options.type, grpc_service=options.service,
                                         show_progress=True)
    diff --git a/docs/html/_modules/bosdyn/client/channel.html b/docs/html/_modules/bosdyn/client/channel.html
    index 00d25f1e0..5255f8595 100644
    --- a/docs/html/_modules/bosdyn/client/channel.html
    +++ b/docs/html/_modules/bosdyn/client/channel.html
    @@ -7,7 +7,7 @@
       
       
       
    -  bosdyn.client.channel — Spot 2.3.5 documentation
    +  bosdyn.client.channel — Spot 3.0.0 documentation
       
     
       
    @@ -66,7 +66,7 @@
                 
                 
                   
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -557,10 +598,10 @@

    Source code for bosdyn.client.channel

     
     from .exceptions import (
         RpcError, ClientCancelledOperationError, InvalidAppTokenError, InvalidClientCertificateError,
    -    RetryableUnavailableError, NonexistentAuthorityError, NotFoundError, PermissionDeniedError,
    -    ProxyConnectionError, ResponseTooLargeError, ServiceFailedDuringExecutionError,
    +    NonexistentAuthorityError, NotFoundError, PermissionDeniedError, ProxyConnectionError,
    +    RetryableUnavailableError, ResponseTooLargeError, ServiceFailedDuringExecutionError,
         ServiceUnavailableError, TimedOutError, UnableToConnectToRobotError, UnauthenticatedError,
    -    UnknownDnsNameError, UnimplementedError, TransientFailureError)
    +    UnknownDnsNameError, UnimplementedError, TooManyRequestsError, TransientFailureError)
     
     TransportError = grpc.RpcError
     
    @@ -568,7 +609,7 @@ 

    Source code for bosdyn.client.channel

     
     # Set default max message length for sending and receiving to 100MB. This value is used when
     # creating channels in the bosdyn.client.Robot class.
    -DEFAULT_MAX_MESSAGE_LENGTH = 100 * (1024 ** 2)
    +DEFAULT_MAX_MESSAGE_LENGTH = 100 * (1024**2)
     
     
     
    [docs]class RefreshingAccessTokenAuthMetadataPlugin(grpc.AuthMetadataPlugin): @@ -578,6 +619,7 @@

    Source code for bosdyn.client.channel

             token_cb: Callable that returns a tuple of (app_token, user_token)
             add_app_token (bool): Whether to include an app token in the metadata. This is necessary for compatibility with old robot software.
         """
    +
         def __init__(self, token_cb, add_app_token):
             self._token_cb = token_cb
             self._add_app_token = add_app_token
    @@ -672,6 +714,8 @@ 

    Source code for bosdyn.client.channel

                 return InvalidAppTokenError(rpc_error, InvalidAppTokenError.__doc__)
             elif str(404) in details:
                 return NotFoundError(rpc_error, NotFoundError.__doc__)
    +        elif str(429) in details:
    +            return TooManyRequestsError(rpc_error, TooManyRequestsError.__doc__)
             elif str(502) in details:
                 return ServiceUnavailableError(rpc_error, ServiceUnavailableError.__doc__)
             elif str(504) in details:
    @@ -708,16 +752,19 @@ 

    Source code for bosdyn.client.channel

             elif 'Connect Failed' in debug or 'Failed to pick subchannel' in debug:
                 # This error should be checked last because a lot of grpc errors contain said substrings.
                 return UnableToConnectToRobotError(rpc_error, UnableToConnectToRobotError.__doc__)
    -    
    +
         if code is grpc.StatusCode.UNAVAILABLE:
    -        if 'Socket closed' in debug:
    +        if 'Socket closed' in debug or 'Connection reset by peer' in debug:
                 return RetryableUnavailableError(rpc_error, RetryableUnavailableError.__doc__)
    +        if str(502) in details:
    +            return ServiceUnavailableError(rpc_error, ServiceUnavailableError.__doc__)
     
         _LOGGER.warning('Unclassified exception: %s', rpc_error)
     
         return RpcError(rpc_error, RpcError.__doc__)
    -
    [docs]def generate_channel_options(max_send_message_length = None, max_receive_message_length = None): + +
    [docs]def generate_channel_options(max_send_message_length=None, max_receive_message_length=None): """Generate the array of options to specify in the creation of a client channel or server. The list contains the values for max allowed message length for both sending and @@ -732,8 +779,8 @@

    Source code for bosdyn.client.channel

         """
     
         return [('grpc.max_send_message_length', max_send_message_length or DEFAULT_MAX_MESSAGE_LENGTH),
    -        ('grpc.max_receive_message_length',
    -            max_receive_message_length or DEFAULT_MAX_MESSAGE_LENGTH)]
    + ('grpc.max_receive_message_length', max_receive_message_length or + DEFAULT_MAX_MESSAGE_LENGTH)]
    diff --git a/docs/html/_modules/bosdyn/client/command_line.html b/docs/html/_modules/bosdyn/client/command_line.html index 409c2839e..8b6bd62a6 100644 --- a/docs/html/_modules/bosdyn/client/command_line.html +++ b/docs/html/_modules/bosdyn/client/command_line.html @@ -7,7 +7,7 @@ - bosdyn.client.command_line — Spot 2.3.5 documentation + bosdyn.client.command_line — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -564,7 +605,7 @@

    Source code for bosdyn.client.command_line

     
     import six
     
    -from bosdyn.api.data_buffer_pb2 import TextMessage
    +from bosdyn.api.data_buffer_pb2 import Event, TextMessage
     from bosdyn.api.data_index_pb2 import EventsCommentsSpec
     from bosdyn.api import data_acquisition_pb2
     import bosdyn.client
    @@ -1452,8 +1493,8 @@ 

    Source code for bosdyn.client.command_line

     
    [docs] def pretty_print(self, values): # pylint: disable=no-self-use last_date_shown = None for comment in values: - dtm = datetime.datetime.fromtimestamp( - comment.timestamp.seconds + comment.timestamp.seconds * 1e-9) + dtm = datetime.datetime.fromtimestamp(comment.timestamp.seconds + + comment.timestamp.seconds * 1e-9) if dtm.date() != last_date_shown: print("\n[{}]".format(dtm.date())) last_date_shown = dtm.date() @@ -1465,6 +1506,21 @@

    Source code for bosdyn.client.command_line

     
         NAME = 'events'
     
    +    def __init__(self, subparsers, command_dict):
    +        """Get operator comments from the robot.
    +
    +        Args:
    +            subparsers: List of argument parsers.
    +            command_dict: Dictionary of command names which take parsed options.
    +        """
    +        super(GetDataBufferEventsCommand, self).__init__(subparsers, command_dict)
    +        self._parser.add_argument('--type', help='query for only the given event-type')
    +        # pylint: disable=no-member
    +        self._parser.add_argument(
    +            '--level',
    +            choices=Event.Level.keys()[1:],  # slice skips UNSET
    +            help='limit level to this and above')
    +
         def _run(self, robot, options):
             """Implementation of the command.
     
    @@ -1477,7 +1533,11 @@ 

    Source code for bosdyn.client.command_line

             """
     
             request_spec = EventsCommentsSpec()
    -        request_spec.events.add()  # pylint: disable=no-member
    +        event_spec = request_spec.events.add()  # pylint: disable=no-member
    +        if options.type:
    +            event_spec.type = options.type
    +        if options.level:
    +            event_spec.level.value = Event.Level.Value(options.level)  # pylint: disable=no-member
     
             def _get_events(response):
                 return response.events_comments.events
    @@ -1830,6 +1890,8 @@ 

    Source code for bosdyn.client.command_line

             super(LicenseCommand, self).__init__(subparsers, command_dict)
             self._parser.add_argument('--proto', action='store_true',
                                       help='print listing in proto format')
    +        self._parser.add_argument('-f', '--feature-codes', nargs='+',
    +                                  help='Optional feature list for GetFeatureEnabled API.')
     
         def _run(self, robot, options):
             """Implementation of the command.
    @@ -1842,13 +1904,27 @@ 

    Source code for bosdyn.client.command_line

                 True.
             """
             license_client = robot.ensure_client(LicenseClient.default_service_name)
    +        self._get_license_info(license_client, options)
    +        self._get_feature_enabled(license_client, options)
    +        return True
    +
    +    def _get_license_info(self, license_client, options):
             license_info = license_client.get_license_info()
             if options.proto:
                 print(license_info)
             else:
                 print(str(license_info))
     
    -        return True
    + def _get_feature_enabled(self, license_client, options): + if not options.feature_codes or len(options.feature_codes) == 0: + return + + feature_enabled = license_client.get_feature_enabled(options.feature_codes) + for feature in feature_enabled: + if feature_enabled[feature]: + print(f"Feature {feature} is enabled.") + else: + print(f"Feature {feature} is not enabled.")
    [docs]class LeaseCommands(Subcommands): @@ -2339,7 +2415,7 @@

    Source code for bosdyn.client.command_line

             self._format_and_print_capability("Data Type", "Data Name", "(optional) Service Name")
             print("-" * (self._data_type_width + self._data_name_width + self._service_name_width))
             for data_name in capabilities.data_sources:
    -            self._format_and_print_capability("data", data_name.name)
    +            self._format_and_print_capability("data", data_name.name, data_name.service_name)
             for img_service in capabilities.image_sources:
                 for img in img_service.image_source_names:
                     self._format_and_print_capability("image", img, img_service.service_name)
    @@ -2376,6 +2452,7 @@ 

    Source code for bosdyn.client.command_line

             print(response)
             return True
    +
    [docs]class HostComputerIPCommand(Command): """Determine a computer's IP address.""" @@ -2401,7 +2478,9 @@

    Source code for bosdyn.client.command_line

             Returns:
                 True
             """
    -        print("The IP address of the computer used to talk to the robot is: %s" %(bosdyn.client.common.get_self_ip(robot._name)))
    + print("The IP address of the computer used to talk to the robot is: %s" % + (bosdyn.client.common.get_self_ip(robot._name)))
    +
    [docs]class PowerCommand(Subcommands): diff --git a/docs/html/_modules/bosdyn/client/common.html b/docs/html/_modules/bosdyn/client/common.html index 7b6bf41ea..20d5e14cf 100644 --- a/docs/html/_modules/bosdyn/client/common.html +++ b/docs/html/_modules/bosdyn/client/common.html @@ -7,7 +7,7 @@ - bosdyn.client.common — Spot 2.3.5 documentation + bosdyn.client.common — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -555,8 +596,10 @@

    Source code for bosdyn.client.common

     import copy
     import functools
     import logging
    +import math
     import types
     import grpc
    +import six
     import socket
     
     from .channel import TransportError, translate_exception
    @@ -564,10 +607,12 @@ 

    Source code for bosdyn.client.common

     
     _LOGGER = logging.getLogger(__name__)
     
    +from bosdyn.api import data_chunk_pb2
     from bosdyn.api import license_pb2
     
     DEFAULT_RPC_TIMEOUT = 30  # seconds
     
    +
     
    [docs]def common_header_errors(response): """Return an exception based on common response header. None if no error.""" if response.header.error.code == response.header.error.CODE_UNSPECIFIED: @@ -734,6 +779,10 @@

    Source code for bosdyn.client.common

     
         return wrapper
    +
    [docs]def maybe_raise(exc): + """raise the provided exception if it is not None""" + if exc is not None: + raise exc
    [docs] @process_kwargs def call(self, rpc_method, request, value_from_response=None, error_from_response=None, @@ -877,14 +928,16 @@

    Source code for bosdyn.client.common

                 timeout = kwargs.pop('timeout', DEFAULT_RPC_TIMEOUT)
                 response = rpc_method(request, timeout=timeout, **kwargs)
             except TransportError as e:
    -            raise translate_exception(e)
    +            # Use the "raise from None" pattern to reset the exception's context, which produces
    +            # confusing stack traces.
    +            six.raise_from(translate_exception(e), None)
     
             if isinstance(rpc_method, grpc.UnaryStreamMultiCallable) or isinstance(
                     rpc_method, grpc.StreamStreamMultiCallable):
                 # The outgoing response is a streaming response.
                 response = self.update_response_iterator(response, logger, rpc_method, is_blocking=True)
    -            return self.handle_response_streaming(
    -                list(response), error_from_response, value_from_response)
    +            return self.handle_response_streaming(list(response), error_from_response,
    +                                                  value_from_response)
             else:
                 response = self._apply_response_processors(response)
                 logger.debug('response: %s %s', rpc_method._method,
    @@ -897,7 +950,7 @@ 

    Source code for bosdyn.client.common

             else:
                 exc = None
             if exc is not None:
    -            raise exc
    +            raise exc  # pylint: disable=raising-bad-type
             if value_from_response is None:
                 return response
             return value_from_response(response)
    @@ -908,7 +961,7 @@

    Source code for bosdyn.client.common

             else:
                 exc = None
             if exc is not None:
    -            raise exc
    +            raise exc  # pylint: disable=raising-bad-type
             if value_from_response is None:
                 return response
             return value_from_response(response)
    @@ -965,7 +1018,23 @@

    Source code for bosdyn.client.common

                 method_name_short = str(method_name).split(BaseClient._SPLIT_METHOD)[-1]
                 # This returns the same instance if it's been created before.
                 return self.logger.getChild(method_name_short)
    -        return self.logger
    + return self.logger + +
    [docs] @staticmethod + def chunk_message(message, data_chunk_byte_size): + """Take a message, and split it into data chunks + Args: + data_chunk_byte_size: max size of each streamed message + """ + serialized = message.SerializeToString() + total_bytes_size = len(serialized) + num_chunks = math.ceil(total_bytes_size / data_chunk_byte_size) + for i in range(num_chunks): + start_index = i * data_chunk_byte_size + end_index = min(total_bytes_size, (i + 1) * data_chunk_byte_size) + chunk = data_chunk_pb2.DataChunk(total_size=total_bytes_size) + chunk.data = serialized[start_index:end_index] + yield chunk
    [docs]class FutureWrapper(): diff --git a/docs/html/_modules/bosdyn/client/data_acquisition.html b/docs/html/_modules/bosdyn/client/data_acquisition.html index f8bd1db16..3ff8c23e2 100644 --- a/docs/html/_modules/bosdyn/client/data_acquisition.html +++ b/docs/html/_modules/bosdyn/client/data_acquisition.html @@ -7,7 +7,7 @@ - bosdyn.client.data_acquisition — Spot 2.3.5 documentation + bosdyn.client.data_acquisition — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -563,9 +604,8 @@

    Source code for bosdyn.client.data_acquisition

    from google.protobuf import json_format from bosdyn.client.exceptions import Error, ResponseError -from bosdyn.client.common import (common_header_errors, error_factory, - handle_common_header_errors, handle_unset_status_error, - error_pair, BaseClient) +from bosdyn.client.common import (common_header_errors, error_factory, handle_common_header_errors, + handle_unset_status_error, error_pair, BaseClient) from bosdyn.api import data_acquisition_pb2 as data_acquisition from bosdyn.api import data_acquisition_service_pb2_grpc as data_acquisition_service @@ -608,6 +648,20 @@

    Source code for bosdyn.client.data_acquisition

    except AttributeError: pass # other doesn't have a time_sync accessor

    +
    [docs] def make_acquire_data_request(self, acquisition_requests, action_name, group_name, data_timestamp=None, metadata=None): + """Helper utility to generate an AcquireDataRequest.""" + if data_timestamp is None: + if not self._timesync_endpoint: + data_timestamp = now_timestamp() + else: + data_timestamp = self._timesync_endpoint.robot_timestamp_from_local_secs( + time.time()) + action_id = data_acquisition.CaptureActionId(action_name=action_name, group_name=group_name, + timestamp=data_timestamp) + return data_acquisition.AcquireDataRequest(acquisition_requests=acquisition_requests, + action_id=action_id, + metadata=metadata_to_proto(metadata))
    +
    [docs] def acquire_data(self, acquisition_requests, action_name, group_name, data_timestamp=None, metadata=None, **kwargs): """Trigger a data acquisition to save data and metadata to the data buffer. @@ -625,48 +679,40 @@

    Source code for bosdyn.client.data_acquisition

    Raises: RpcError: Problem communicating with the robot. + ValueError: Metadata is not in the right format. Returns: If the RPC is successful, then it will return the acquire data request id, which can be used to check the status of the acquisition and get feedback. """ - - if data_timestamp is None: - if not self._timesync_endpoint: - data_timestamp = now_timestamp() - else: - data_timestamp = self._timesync_endpoint.robot_timestamp_from_local_secs( - time.time()) - action_id = data_acquisition.CaptureActionId(action_name=action_name, - group_name=group_name, timestamp=data_timestamp) - - metadata_proto = metadata_to_proto(metadata) - request = data_acquisition.AcquireDataRequest(metadata=metadata_proto, - acquisition_requests=acquisition_requests, - action_id=action_id) + request = self.make_acquire_data_request(acquisition_requests, action_name, group_name, + data_timestamp, metadata) return self.call(self._stub.AcquireData, request, value_from_response=get_request_id, error_from_response=acquire_data_error, **kwargs)

    -
    [docs] def acquire_data_async(self, acquisition_requests, action_name, group_name, - data_timestamp=None, metadata=None, **kwargs): +
    [docs] def acquire_data_async(self, acquisition_requests, action_name, group_name, data_timestamp=None, + metadata=None, **kwargs): """Async version of the acquire_data() RPC.""" - if data_timestamp is None: - if not self._timesync_endpoint: - data_timestamp = now_timestamp() - else: - data_timestamp = self._timesync_endpoint.robot_timestamp_from_local_secs( - time.time()) - action_id = data_acquisition.CaptureActionId(action_name=action_name, - group_name=group_name, timestamp=data_timestamp) + request = self.make_acquire_data_request(acquisition_requests, action_name, group_name, + data_timestamp, metadata) + return self.call_async(self._stub.AcquireData, request, value_from_response=get_request_id, + error_from_response=acquire_data_error, **kwargs)
    - metadata_proto = metadata_to_proto(metadata) - request = data_acquisition.AcquireDataRequest(metadata=metadata_proto, - acquisition_requests=acquisition_requests, - action_id=action_id) +
    [docs] def acquire_data_from_request(self, request, **kwargs): + """Alternate version of acquire_data() that takes an AcquireDataRequest directly. + + Returns: + If the RPC is successful, then it will return the AcquireDataResponse. + """ + return self.call(self._stub.AcquireData, request, + error_from_response=acquire_data_error, **kwargs)
    + +
    [docs] def acquire_data_from_request_async(self, request, **kwargs): + """Async version of acquire_data_from_request().""" return self.call_async(self._stub.AcquireData, request, - value_from_response=get_request_id, error_from_response=acquire_data_error, **kwargs)
    +
    [docs] def get_status(self, request_id, **kwargs): """Check the status of a data acquisition based on the request id. @@ -688,8 +734,8 @@

    Source code for bosdyn.client.data_acquisition

    [docs] def get_status_async(self, request_id, **kwargs): """Async version of the get_status() RPC.""" request = data_acquisition.GetStatusRequest(request_id=request_id) - return self.call_async(self._stub.GetStatus, request, - error_from_response=_get_status_error, **kwargs)

    + return self.call_async(self._stub.GetStatus, request, error_from_response=_get_status_error, + **kwargs)
    [docs] def get_service_info(self, **kwargs): """Get information from a DAQ service to list it's capabilities - which data, metadata, @@ -727,8 +773,8 @@

    Source code for bosdyn.client.data_acquisition

    status as well as other information about any possible errors. """ request = data_acquisition.CancelAcquisitionRequest(request_id=request_id) - return self.call(self._stub.CancelAcquisition, request, error_from_response=_cancel_acquisition_error, - **kwargs)

    + return self.call(self._stub.CancelAcquisition, request, + error_from_response=_cancel_acquisition_error, **kwargs)
    [docs] def cancel_acquisition_async(self, request_id, **kwargs): """Async version of the cancel_acquisition() RPC.""" @@ -737,8 +783,8 @@

    Source code for bosdyn.client.data_acquisition

    error_from_response=_cancel_acquisition_error, **kwargs)

    -_ACQUIRE_DATA_STATUS_TO_ERROR = collections.defaultdict( - lambda: (DataAcquisitionResponseError, None)) +_ACQUIRE_DATA_STATUS_TO_ERROR = collections.defaultdict(lambda: + (DataAcquisitionResponseError, None)) _ACQUIRE_DATA_STATUS_TO_ERROR.update({ data_acquisition.AcquireDataResponse.STATUS_OK: (None, None), @@ -760,6 +806,7 @@

    Source code for bosdyn.client.data_acquisition

    error_pair(CancellationFailedError) }) +

    [docs]def metadata_to_proto(metadata): """Checks the type to determine if a conversion is required to create a bosdyn.api.Metadata proto message. @@ -769,16 +816,23 @@

    Source code for bosdyn.client.data_acquisition

    with the data returned by the DataAcquisitionService when logged in the data buffer service. + Raises: + ValueError: Metadata is not in the right format. + Returns: If metadata is provided, this will return a protobuf Metadata message. Otherwise it will return None. """ + if metadata is None: + return None metadata_proto = None if isinstance(metadata, data_acquisition.Metadata): metadata_proto = metadata elif isinstance(metadata, dict): metadata_proto = data_acquisition.Metadata() metadata_proto.data.update(metadata) + else: + raise ValueError('Invalid metadata, not a dict or data_acquisition.Metadata') return metadata_proto

    @@ -787,8 +841,8 @@

    Source code for bosdyn.client.data_acquisition

    def acquire_data_error(response): """Return a custom exception based on the AcquireData response, None if no error.""" return error_factory(response, response.status, - status_to_string=data_acquisition.AcquireDataResponse.Status.Name, - status_to_error=_ACQUIRE_DATA_STATUS_TO_ERROR)

    + status_to_string=data_acquisition.AcquireDataResponse.Status.Name, + status_to_error=_ACQUIRE_DATA_STATUS_TO_ERROR)
    @handle_common_header_errors @@ -796,20 +850,23 @@

    Source code for bosdyn.client.data_acquisition

    def _get_status_error(response): """Return a custom exception based on the GetStatus response, None if no error.""" return error_factory(response, response.status, - status_to_string=data_acquisition.GetStatusResponse.Status.Name, - status_to_error=_GET_STATUS_STATUS_TO_ERROR) + status_to_string=data_acquisition.GetStatusResponse.Status.Name, + status_to_error=_GET_STATUS_STATUS_TO_ERROR) + @handle_common_header_errors @handle_unset_status_error(unset='STATUS_UNKNOWN') def _cancel_acquisition_error(response): """Return a custom exception based on the CancelAcquisition response, None if no error.""" return error_factory(response, response.status, - status_to_string=data_acquisition.CancelAcquisitionResponse.Status.Name, - status_to_error=_CANCEL_ACQUISITION_STATUS_TO_ERROR) + status_to_string=data_acquisition.CancelAcquisitionResponse.Status.Name, + status_to_error=_CANCEL_ACQUISITION_STATUS_TO_ERROR) + def _get_service_info_capabilities(response): return response.capabilities +

    [docs]def get_request_id(response): return response.request_id
    diff --git a/docs/html/_modules/bosdyn/client/data_acquisition_helpers.html b/docs/html/_modules/bosdyn/client/data_acquisition_helpers.html index f442bc76d..123a0fc0a 100644 --- a/docs/html/_modules/bosdyn/client/data_acquisition_helpers.html +++ b/docs/html/_modules/bosdyn/client/data_acquisition_helpers.html @@ -7,7 +7,7 @@ - bosdyn.client.data_acquisition_helpers — Spot 2.3.5 documentation + bosdyn.client.data_acquisition_helpers — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -573,8 +614,9 @@

    Source code for bosdyn.client.data_acquisition_helpers

    # Logger for all the debug information from the tests. _LOGGER = logging.getLogger() -
    [docs]def issue_acquire_data_request(data_acq_client, acquisition_requests, group_name, - action_name, metadata=None): + +
    [docs]def issue_acquire_data_request(data_acq_client, acquisition_requests, group_name, action_name, + metadata=None): """Sends the data acquisition request without blocking until the acquisition completes. Args: @@ -589,19 +631,21 @@

    Source code for bosdyn.client.data_acquisition_helpers

    indicates the AcquireData rpc failed. """ # Create action id for the query for this request. - action_id = data_acquisition_pb2.CaptureActionId(action_name=action_name, - group_name=group_name) + action_id = data_acquisition_pb2.CaptureActionId(action_name=action_name, group_name=group_name) # Send an AcquireData request request_id = None try: request_id = data_acq_client.acquire_data(acquisition_requests=acquisition_requests, - action_name=action_name, group_name=action_id.group_name, metadata=metadata) + action_name=action_name, + group_name=action_id.group_name, + metadata=metadata) except ResponseError as err: print("Exception raised by issue_acquire_data_request: " + str(err)) return request_id, action_id
    +
    [docs]def acquire_and_process_request(data_acquisition_client, acquisition_requests, group_name, action_name, metadata=None, block_until_complete=True): """Send acquisition request and optionally block until the acquisition completes. @@ -621,8 +665,9 @@

    Source code for bosdyn.client.data_acquisition_helpers

    Boolean indicating if the acquisition completed successfully or not. """ # Make the acquire data request. This will return our current request id. - request_id, action_id = issue_acquire_data_request(data_acquisition_client, acquisition_requests, - group_name, action_name, metadata) + request_id, action_id = issue_acquire_data_request(data_acquisition_client, + acquisition_requests, group_name, + action_name, metadata) if not request_id: # The AcquireData request failed for some reason. No need to attempt to @@ -642,7 +687,7 @@

    Source code for bosdyn.client.data_acquisition_helpers

    print("Exception: %s" % str(err)) return False print("Current status is: %s" % - data_acquisition_pb2.GetStatusResponse.Status.Name(get_status_response.status)) + data_acquisition_pb2.GetStatusResponse.Status.Name(get_status_response.status)) if get_status_response.status == data_acquisition_pb2.GetStatusResponse.STATUS_COMPLETE: return True if get_status_response.status == data_acquisition_pb2.GetStatusResponse.STATUS_TIMEDOUT: @@ -652,11 +697,13 @@

    Source code for bosdyn.client.data_acquisition_helpers

    print("Data error was received: %s" % get_status_response) return False if get_status_response.status == data_acquisition_pb2.GetStatusResponse.STATUS_REQUEST_ID_DOES_NOT_EXIST: - print("The acquisition request id %s is unknown: %s" % (request_id, get_status_response)) + print( + "The acquisition request id %s is unknown: %s" % (request_id, get_status_response)) return False time.sleep(0.2) return True
    +
    [docs]def cancel_acquisition_request(data_acq_client, request_id): """Cancels an acquisition request based on the request id @@ -675,9 +722,10 @@

    Source code for bosdyn.client.data_acquisition_helpers

    try: is_cancelled_response = data_acq_client.cancel_acquisition(request_id) print("Status of the request to cancel the data-acquisition in progress: " + - data_acquisition_pb2.CancelAcquisitionResponse.Status.Name(is_cancelled_response.status)) + data_acquisition_pb2.CancelAcquisitionResponse.Status.Name( + is_cancelled_response.status)) except ResponseError as err: - print("ResponseError raised when cancelling: "+str(err)) + print("ResponseError raised when cancelling: " + str(err)) # Don't attempt to wait for the cancellation success status. return @@ -691,11 +739,12 @@

    Source code for bosdyn.client.data_acquisition_helpers

    break print("Request " + str(request_id) + " status: " + - data_acquisition_pb2.GetStatusResponse.Status.Name(get_status_response.status)) + data_acquisition_pb2.GetStatusResponse.Status.Name(get_status_response.status)) if get_status_response.status == data_acquisition_pb2.GetStatusResponse.STATUS_ACQUISITION_CANCELLED: print("The request is fully cancelled.") break
    +
    [docs]def clean_filename(filename): """Removes bad characters in a filename. @@ -708,6 +757,7 @@

    Source code for bosdyn.client.data_acquisition_helpers

    return "".join(i for i in filename if i not in ":*?<>|")
    +
    [docs]def make_time_query_params(start_time_secs, end_time_secs, robot): """Create time-based query params for the download request. @@ -724,9 +774,10 @@

    Source code for bosdyn.client.data_acquisition_helpers

    print(from_timestamp.ToJsonString(), to_timestamp.ToJsonString()) query_params = data_acquisition_store_pb2.DataQueryParams( time_range=data_acquisition_store_pb2.TimeRangeQuery(from_timestamp=from_timestamp, - to_timestamp=to_timestamp)) + to_timestamp=to_timestamp)) return query_params
    +
    [docs]def make_time_query_params_from_group_name(group_name, data_store_client): """Create time-based query params for the download request using the group name. @@ -745,7 +796,8 @@

    Source code for bosdyn.client.data_acquisition_helpers

    try: saved_capture_actions = data_store_client.list_capture_actions(query_params) except Exception as err: - _LOGGER.error("Failed to list the capture action ids for group_name %s: %s", group_name, err) + _LOGGER.error("Failed to list the capture action ids for group_name %s: %s", group_name, + err) return None # Filter all the CaptureActionIds for the start/end time. These end times are already in @@ -765,7 +817,8 @@

    Source code for bosdyn.client.data_acquisition_helpers

    end_time = (time_secs, timestamp) if not (start_time and end_time): - _LOGGER.error("Could not find a start/end time from the list of capture action ids: %s", saved_capture_actions) + _LOGGER.error("Could not find a start/end time from the list of capture action ids: %s", + saved_capture_actions) return None # Ensure the timestamps are ordered correctly and the @@ -775,14 +828,16 @@

    Source code for bosdyn.client.data_acquisition_helpers

    start_time[1].seconds -= 3 end_time[1].seconds += 3 - _LOGGER.info("Downloading data with a start time of %s seconds and end time of %s seconds.", start_time[0], end_time[0]) + _LOGGER.info("Downloading data with a start time of %s seconds and end time of %s seconds.", + start_time[0], end_time[0]) # Make the download data request with a time query parameter. query_params = data_acquisition_store_pb2.DataQueryParams( time_range=data_acquisition_store_pb2.TimeRangeQuery(from_timestamp=start_time[1], - to_timestamp=end_time[1])) + to_timestamp=end_time[1])) return query_params
    +
    [docs]def download_data_REST(query_params, hostname, token, destination_folder='.', additional_params=None): """Retrieve all data for a query from the DataBuffer REST API and write it to files. @@ -808,18 +863,20 @@

    Source code for bosdyn.client.data_acquisition_helpers

    headers = {"Authorization": "Bearer {}".format(token)} get_params = additional_params or {} if query_params.HasField('time_range'): - get_params.update({'from_nsec': query_params.time_range.from_timestamp.ToNanoseconds(), - 'to_nsec': query_params.time_range.to_timestamp.ToNanoseconds()}) - chunk_size = 10 * (1024 ** 2) # This value is not guaranteed. + get_params.update({ + 'from_nsec': query_params.time_range.from_timestamp.ToNanoseconds(), + 'to_nsec': query_params.time_range.to_timestamp.ToNanoseconds() + }) + chunk_size = 10 * (1024**2) # This value is not guaranteed. with requests.get(url, verify=False, stream=True, headers=headers, - params=get_params) as resp: + params=get_params) as resp: print("Download request HTTPS status code: %s" % resp.status_code) # This is the default file name used to download data, updated from response. if resp.status_code == 204: print("No content available for the specified download time range (in seconds): " - "[%d, %d]"% (query_params.time_range.from_timestamp.ToNanoseconds()/1.0e9, - query_params.time_range.to_timestamp.ToNanoseconds()/1.0e9)) + "[%d, %d]" % (query_params.time_range.from_timestamp.ToNanoseconds() / 1.0e9, + query_params.time_range.to_timestamp.ToNanoseconds() / 1.0e9)) return False download_file = Path(folder, "download.zip") content = resp.headers['Content-Disposition'] @@ -837,7 +894,7 @@

    Source code for bosdyn.client.data_acquisition_helpers

    with open(str(download_file), 'wb') as fid: for chunk in resp.iter_content(chunk_size=chunk_size): - print('.', end = '', flush=True) + print('.', end='', flush=True) fid.write(chunk) except requests.exceptions.HTTPError as rest_error: print("REST Exception:\n") diff --git a/docs/html/_modules/bosdyn/client/data_acquisition_plugin.html b/docs/html/_modules/bosdyn/client/data_acquisition_plugin.html index c9a6f1bbd..bd94ffc99 100644 --- a/docs/html/_modules/bosdyn/client/data_acquisition_plugin.html +++ b/docs/html/_modules/bosdyn/client/data_acquisition_plugin.html @@ -7,7 +7,7 @@ - bosdyn.client.data_acquisition_plugin — Spot 2.3.5 documentation + bosdyn.client.data_acquisition_plugin — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -599,6 +640,7 @@

    Source code for bosdyn.client.data_acquisition_plugin

    Raises: RpcError: Problem communicating with the robot. + ValueError: Metadata is not in the right format. Returns: If the RPC is successful, then it will return the acquire data response, which can be @@ -613,7 +655,7 @@

    Source code for bosdyn.client.data_acquisition_plugin

    error_from_response=acquire_data_error, **kwargs)
    [docs] def acquire_plugin_data_async(self, acquisition_requests, action_id, data_identifiers=None, - metadata=None, **kwargs): + metadata=None, **kwargs): """Async version of the acquire_plugin_data() RPC.""" metadata_proto = metadata_to_proto(metadata) diff --git a/docs/html/_modules/bosdyn/client/data_acquisition_plugin_service.html b/docs/html/_modules/bosdyn/client/data_acquisition_plugin_service.html index 2e5f8d339..e97cfc714 100644 --- a/docs/html/_modules/bosdyn/client/data_acquisition_plugin_service.html +++ b/docs/html/_modules/bosdyn/client/data_acquisition_plugin_service.html @@ -7,7 +7,7 @@ - bosdyn.client.data_acquisition_plugin_service — Spot 2.3.5 documentation + bosdyn.client.data_acquisition_plugin_service — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -602,8 +643,7 @@

    Source code for bosdyn.client.data_acquisition_plugin_service

    from bosdyn.client import Robot from bosdyn.client.data_acquisition_store import DataAcquisitionStoreClient from bosdyn.client.data_buffer import DataBufferClient -from bosdyn.client.util import populate_response_header -from bosdyn.client.server_util import ResponseContext +from bosdyn.client.server_util import ResponseContext, populate_response_header _LOGGER = logging.getLogger(__name__) @@ -813,8 +853,8 @@

    Source code for bosdyn.client.data_acquisition_plugin_service

    if future.exception() is None: self.state.add_saved([data_id]) else: - self.state.add_errors([ - make_error(data_id, 'Failed to store data: {}'.format(future.exception()))]) + self.state.add_errors( + [make_error(data_id, 'Failed to store data: {}'.format(future.exception()))]) return not self.state.has_data_errors()
    diff --git a/docs/html/_modules/bosdyn/client/data_acquisition_store.html b/docs/html/_modules/bosdyn/client/data_acquisition_store.html index de5fdc7de..87926ecda 100644 --- a/docs/html/_modules/bosdyn/client/data_acquisition_store.html +++ b/docs/html/_modules/bosdyn/client/data_acquisition_store.html @@ -7,7 +7,7 @@ - bosdyn.client.data_acquisition_store — Spot 2.3.5 documentation + bosdyn.client.data_acquisition_store — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -563,9 +604,8 @@

    Source code for bosdyn.client.data_acquisition_store

    from google.protobuf import json_format from bosdyn.client.exceptions import Error, ResponseError -from bosdyn.client.common import (common_header_errors, error_factory, - handle_common_header_errors, handle_unset_status_error, - error_pair, BaseClient) +from bosdyn.client.common import (common_header_errors, error_factory, handle_common_header_errors, + handle_unset_status_error, error_pair, BaseClient) from bosdyn.api import data_acquisition_store_pb2 as data_acquisition_store from bosdyn.api import data_acquisition_store_service_pb2_grpc as data_acquisition_store_service from bosdyn.api import image_pb2 @@ -593,7 +633,6 @@

    Source code for bosdyn.client.data_acquisition_store

    except AttributeError: pass # other doesn't have a time_sync accessor
    -
    [docs] def list_capture_actions(self, query, **kwargs): """List capture actions that satisfy the query parameters. @@ -616,7 +655,6 @@

    Source code for bosdyn.client.data_acquisition_store

    value_from_response=_get_action_ids, error_from_response=common_header_errors, **kwargs)
    -
    [docs] def list_stored_images(self, query, **kwargs): """List images that satisfy the query parameters. @@ -638,7 +676,6 @@

    Source code for bosdyn.client.data_acquisition_store

    value_from_response=_get_data_ids, error_from_response=common_header_errors, **kwargs)
    -
    [docs] def list_stored_metadata(self, query, **kwargs): """List metadata that satisfy the query parameters. @@ -660,7 +697,6 @@

    Source code for bosdyn.client.data_acquisition_store

    value_from_response=_get_data_ids, error_from_response=common_header_errors, **kwargs)
    -
    [docs] def list_stored_data(self, query, **kwargs): """List data that satisfy the query parameters. @@ -682,7 +718,6 @@

    Source code for bosdyn.client.data_acquisition_store

    value_from_response=_get_data_ids, error_from_response=common_header_errors, **kwargs)
    -
    [docs] def store_image(self, image, data_id, **kwargs): """Store image. @@ -695,9 +730,8 @@

    Source code for bosdyn.client.data_acquisition_store

    """ request = data_acquisition_store.StoreImageRequest(image=image, data_id=data_id) - return self.call(self._stub.StoreImage, request, - error_from_response=common_header_errors, **kwargs)
    - + return self.call(self._stub.StoreImage, request, error_from_response=common_header_errors, + **kwargs)
    [docs] def store_image_async(self, image, data_id, **kwargs): """Async version of the store_image() RPC.""" @@ -705,7 +739,6 @@

    Source code for bosdyn.client.data_acquisition_store

    return self.call_async(self._stub.StoreImage, request, error_from_response=common_header_errors, **kwargs)
    -
    [docs] def store_metadata(self, associated_metadata, data_id, **kwargs): """Store metadata. @@ -721,19 +754,17 @@

    Source code for bosdyn.client.data_acquisition_store

    """ request = data_acquisition_store.StoreMetadataRequest(metadata=associated_metadata, - data_id=data_id) + data_id=data_id) return self.call(self._stub.StoreMetadata, request, error_from_response=common_header_errors, **kwargs)
    -
    [docs] def store_metadata_async(self, associated_metadata, data_id, **kwargs): """Async version of the store_metadata() RPC.""" request = data_acquisition_store.StoreMetadataRequest(metadata=associated_metadata, - data_id=data_id) + data_id=data_id) return self.call_async(self._stub.StoreMetadata, request, error_from_response=common_header_errors, **kwargs)
    -
    [docs] def store_data(self, data, data_id, file_extension=None, **kwargs): """Store data. @@ -747,15 +778,14 @@

    Source code for bosdyn.client.data_acquisition_store

    """ request = data_acquisition_store.StoreDataRequest(data=data, data_id=data_id, - file_extension=file_extension) - return self.call(self._stub.StoreData, request, - error_from_response=common_header_errors, **kwargs)
    - + file_extension=file_extension) + return self.call(self._stub.StoreData, request, error_from_response=common_header_errors, + **kwargs)
    [docs] def store_data_async(self, data, data_id, file_extension=None, **kwargs): """Async version of the store_data() RPC.""" request = data_acquisition_store.StoreDataRequest(data=data, data_id=data_id, - file_extension=file_extension) + file_extension=file_extension) return self.call_async(self._stub.StoreData, request, error_from_response=common_header_errors, **kwargs)
    @@ -763,12 +793,15 @@

    Source code for bosdyn.client.data_acquisition_store

    def _get_action_ids(response): return response.action_ids + def _get_data_ids(response): return response.data_ids + def _get_image(response): return response.image + def _get_metadata(response): return response.metadata
    diff --git a/docs/html/_modules/bosdyn/client/data_buffer.html b/docs/html/_modules/bosdyn/client/data_buffer.html index ed09c7795..c2a489306 100644 --- a/docs/html/_modules/bosdyn/client/data_buffer.html +++ b/docs/html/_modules/bosdyn/client/data_buffer.html @@ -7,7 +7,7 @@ - bosdyn.client.data_buffer — Spot 2.3.5 documentation + bosdyn.client.data_buffer — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -561,6 +602,7 @@

    Source code for bosdyn.client.data_buffer

     
     import functools
     import time
    +import uuid
     
     from bosdyn.client.exceptions import Error
     from bosdyn.client.common import BaseClient, common_header_errors
    @@ -573,6 +615,56 @@ 

    Source code for bosdyn.client.data_buffer

         """A given argument could not be used."""
    +
    [docs]def log_event( # pylint: disable=too-many-arguments,no-member + robot, event_type, level, description, start_timestamp_secs, + end_timestamp_secs=None, id_str=None, parameters=None, + log_preserve_hint=data_buffer_protos.Event.LOG_PRESERVE_HINT_NORMAL): + """Add an Event to the Data Buffer. + + Args: + robot: A Robot object. + event_type (string): The type of event. + level (bosdyn.api.Event.Level): The relative importance of the event. + description (string): A human-readable description of the event. + start_timestamp_secs (float): Start of the event, in local time. + end_timestamp_secs (float): End of the event. start_timestamp_secs is used if None. + id_str (string): Unique id for event. A uuid is generated if None. + parameters ([bosdyn.api.Parameter]): Parameters to attach to the event. + log_preserve_hint (bosdyn.api.LogPreserveHint): Whether event should try to preserve log data. + """ + + data_buffer_client = robot.ensure_client(DataBufferClient.default_service_name) + + if not id_str: + id_str = str(uuid.uuid1()) + + robot.time_sync.wait_for_sync() + robot_start_timestamp = robot.time_sync.robot_timestamp_from_local_secs(start_timestamp_secs) + if end_timestamp_secs: + robot_end_timestamp = robot.time_sync.robot_timestamp_from_local_secs(end_timestamp_secs) + else: + robot_end_timestamp = robot_start_timestamp + + # pylint: disable=no-member + if isinstance(log_preserve_hint, bool): + if log_preserve_hint: + log_preserve_hint = data_buffer_protos.Event.LOG_PRESERVE_HINT_PRESERVE + else: + log_preserve_hint = data_buffer_protos.Event.LOG_PRESERVE_HINT_NORMAL + + event = data_buffer_protos.Event( + type=event_type, description=description, source=robot.client_name, + id=id_str, start_time=robot_start_timestamp, end_time=robot_end_timestamp, + level=level, log_preserve_hint=log_preserve_hint) + + if parameters: + for parameter in parameters: + proto = event.parameters.add() + proto.CopyFrom(parameter) + + data_buffer_client.add_events([event])
    + +
    [docs]class DataBufferClient(BaseClient): """A client for adding to robot data buffer.""" @@ -612,6 +704,7 @@

    Source code for bosdyn.client.data_buffer

             """Internal text message RPC stub call."""
             request = data_buffer_protos.RecordTextMessagesRequest()
             for in_text_msg in text_messages:
    +            # pylint: disable=no-member
                 request.text_messages.add().CopyFrom(in_text_msg)
     
             return func(self._stub.RecordTextMessages, request, value_from_response=None,
    @@ -638,6 +731,7 @@ 

    Source code for bosdyn.client.data_buffer

             """Internal operator comment RPC stub call."""
             request = data_buffer_protos.RecordOperatorCommentsRequest()
             robot_timestamp = robot_timestamp or self._now_in_robot_basis(msg_type="Operator Comment")
    +        # pylint: disable=no-member
             request.operator_comments.add(message=msg, timestamp=robot_timestamp)
             return func(self._stub.RecordOperatorComments, request, value_from_response=None,
                         error_from_response=common_header_errors, **kwargs)
    @@ -648,10 +742,11 @@ 

    Source code for bosdyn.client.data_buffer

     
             Args:
                 data (bytes): Binary data of one blob.
    -            type_id (string): Type of binary data of blob. For example, this could be the full name of
    -                            a protobuf message type.
    -            channel (string): The name by which messages are typically queried: often the same as
    -                            type_id, or of the form '{prefix}/{type_id}'.
    +            type_id (string): Type of binary data of blob. For example, this could
    +                               be the full name of a protobuf message type.
    +            channel (string): The name by which messages are typically queried:
    +                               often the same as type_id, or of the form
    +                               '{prefix}/{type_id}'.
                 robot_timestamp (google.protobuf.Timestamp): Time of messages, in *robot time*.
     
             Raises:
    @@ -675,8 +770,11 @@ 

    Source code for bosdyn.client.data_buffer

                 channel = type_id
     
             robot_timestamp = robot_timestamp or self._now_in_robot_basis(msg_type=type_id)
    -        request.blob_data.add(timestamp=robot_timestamp, channel=channel, type_id=type_id,
    -                              data=data)
    +
    +        request.blob_data.add(  # pylint: disable=no-member
    +            timestamp=robot_timestamp, channel=channel, type_id=type_id, data=data)
    +
    +        request.sync = write_sync
     
             request.sync = write_sync
     
    @@ -730,7 +828,7 @@ 

    Source code for bosdyn.client.data_buffer

             request = data_buffer_protos.RecordEventsRequest()
     
             for event in events:
    -            request.events.add().CopyFrom(event)
    +            request.events.add().CopyFrom(event)  # pylint: disable=no-member
     
             return func(self._stub.RecordEvents, request, value_from_response=None,
                         error_from_response=common_header_errors, **kwargs)
    @@ -739,7 +837,8 @@ 

    Source code for bosdyn.client.data_buffer

             """Log signal schema to the robot.
     
             Args:
    -            variables (List[SignalSchema.Variable]): List of SignalSchema variables defining what is in tick.
    +            variables (List[SignalSchema.Variable]): List of SignalSchema variables
    +                                                      defining what is in tick.
                 schema_name (string): Name of schema (defined previously by client).
     
             Raises:
    @@ -755,7 +854,7 @@ 

    Source code for bosdyn.client.data_buffer

             """Internal register stub call."""
             tick_schema = data_buffer_protos.SignalSchema(vars=variables, schema_name=schema_name)
             request = data_buffer_protos.RegisterSignalSchemaRequest()
    -        request.schema.CopyFrom(tick_schema)
    +        request.schema.CopyFrom(tick_schema)  # pylint: disable=no-member
             # Schemas are saved internally, according to their schema ID. We need to wait for the
             # response from the server to get the schema id. The response does not include the schema
             # itself so use a partial to process the response appropriately.
    @@ -764,8 +863,9 @@ 

    Source code for bosdyn.client.data_buffer

                         value_from_response=value_from_response,
                         error_from_response=common_header_errors, **kwargs)
     
    -
    [docs] def add_signal_tick(self, data, schema_id, encoding=data_buffer_protos.SignalTick.ENCODING_RAW, - sequence_id=0, source="client", **kwargs): +
    [docs] def add_signal_tick( # pylint: disable=too-many-arguments,no-member + self, data, schema_id, encoding=data_buffer_protos.SignalTick.ENCODING_RAW, + sequence_id=0, source="client", **kwargs): """Log signal data to the robot data buffer. Schema should be sent before any ticks. @@ -784,21 +884,23 @@

    Source code for bosdyn.client.data_buffer

             return self._do_add_signal_tick(self.call, data, schema_id, encoding, sequence_id, source,
                                             **kwargs)
    -
    [docs] def add_signal_tick_async(self, data, schema_id, - encoding=data_buffer_protos.SignalTick.ENCODING_RAW, sequence_id=0, - source="client", **kwargs): +
    [docs] def add_signal_tick_async( # pylint: disable=too-many-arguments,no-member + self, data, schema_id, encoding=data_buffer_protos.SignalTick.ENCODING_RAW, + sequence_id=0, source="client", **kwargs): """Async version of add_signal_tick.""" return self._do_add_signal_tick(self.call_async, data, schema_id, encoding, sequence_id, source, **kwargs)
    - def _do_add_signal_tick(self, func, data, schema_id, encoding, sequence_id, source, **kwargs): + def _do_add_signal_tick( # pylint: disable=too-many-arguments + self, func, data, schema_id, encoding, sequence_id, source, **kwargs): """Internal add signal tick stub call.""" if schema_id not in self.log_tick_schemas: raise LookupError('The log tick schema id "{}" is unknown'.format(schema_id)) request = data_buffer_protos.RecordSignalTicksRequest() - request.tick_data.add(sequence_id=sequence_id, source=source, schema_id=schema_id, - encoding=encoding, data=data) + request.tick_data.add( # pylint: disable=no-member + sequence_id=sequence_id, source=source, schema_id=schema_id, encoding=encoding, + data=data) return func(self._stub.RecordSignalTicks, request, value_from_response=None, error_from_response=common_header_errors, **kwargs) diff --git a/docs/html/_modules/bosdyn/client/data_service.html b/docs/html/_modules/bosdyn/client/data_service.html index 53c6aff56..8cc622865 100644 --- a/docs/html/_modules/bosdyn/client/data_service.html +++ b/docs/html/_modules/bosdyn/client/data_service.html @@ -7,7 +7,7 @@ - bosdyn.client.data_service — Spot 2.3.5 documentation + bosdyn.client.data_service — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -570,7 +611,7 @@

    Source code for bosdyn.client.data_service

     
    [docs]class DataServiceClient(BaseClient): """A client for adding to robot data buffer.""" - default_service_name = 'data-service' + default_service_name = 'data' service_type = 'bosdyn.api.DataService' def __init__(self): diff --git a/docs/html/_modules/bosdyn/client/directory.html b/docs/html/_modules/bosdyn/client/directory.html index 9272b1dd7..7ffca16dc 100644 --- a/docs/html/_modules/bosdyn/client/directory.html +++ b/docs/html/_modules/bosdyn/client/directory.html @@ -7,7 +7,7 @@ - bosdyn.client.directory — Spot 2.3.5 documentation + bosdyn.client.directory — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -662,7 +703,7 @@

    Source code for bosdyn.client.directory

     _STATUS_TO_ERROR.update({
         directory_pb2.GetServiceEntryResponse.STATUS_OK: (None, None),
         directory_pb2.GetServiceEntryResponse.STATUS_NONEXISTENT_SERVICE:
    -    (NonexistentServiceError, NonexistentServiceError.__doc__),
    +        (NonexistentServiceError, NonexistentServiceError.__doc__),
     })
     
     
    diff --git a/docs/html/_modules/bosdyn/client/directory_registration.html b/docs/html/_modules/bosdyn/client/directory_registration.html
    index 66934f1f0..44c56cd80 100644
    --- a/docs/html/_modules/bosdyn/client/directory_registration.html
    +++ b/docs/html/_modules/bosdyn/client/directory_registration.html
    @@ -7,7 +7,7 @@
       
       
       
    -  bosdyn.client.directory_registration — Spot 2.3.5 documentation
    +  bosdyn.client.directory_registration — Spot 3.0.0 documentation
       
     
       
    @@ -66,7 +66,7 @@
                 
                 
                   
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -568,7 +609,6 @@

    Source code for bosdyn.client.directory_registration

    handle_common_header_errors) from .exceptions import ResponseError, TimedOutError, RetryableUnavailableError - _LOGGER = logging.getLogger(__name__) diff --git a/docs/html/_modules/bosdyn/client/docking.html b/docs/html/_modules/bosdyn/client/docking.html index 95fc987d1..9d7856ab0 100644 --- a/docs/html/_modules/bosdyn/client/docking.html +++ b/docs/html/_modules/bosdyn/client/docking.html @@ -7,7 +7,7 @@ - bosdyn.client.docking — Spot 2.3.5 documentation + bosdyn.client.docking — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -556,12 +597,17 @@

    Source code for bosdyn.client.docking

     import collections
     import time
     
    +from deprecated import deprecated
    +
     from bosdyn.api.docking import docking_pb2, docking_service_pb2_grpc
     from bosdyn.client import lease
    -from bosdyn.client.common import (BaseClient, error_factory, handle_common_header_errors,
    -                                  handle_lease_use_result_errors, handle_unset_status_error)
    +from bosdyn.client.common import (BaseClient, common_header_errors, common_lease_errors,
    +                                  error_factory, handle_common_header_errors,
    +                                  handle_lease_use_result_errors, handle_unset_status_error,
    +                                  maybe_raise)
     from bosdyn.client.exceptions import ResponseError
     from bosdyn.client.robot_command import CommandFailedError
    +from bosdyn.util import now_sec, seconds_to_timestamp
     
     
     
    [docs]class DockingClient(BaseClient): @@ -611,21 +657,64 @@

    Source code for bosdyn.client.docking

             return self.call_async(self._stub.DockingCommand, req, self._docking_id_from_response,
                                    _docking_command_error_from_response, **kwargs)
    +
    [docs] def docking_command_full(self, station_id, clock_identifier, end_time, prep_pose_behavior=None, + lease=None, **kwargs): + """Identical to docking_command(), except will return the full DockingCommandResponse.""" + req = self._docking_command_request(lease, station_id, clock_identifier, end_time, + prep_pose_behavior) + return self.call(self._stub.DockingCommand, req, + error_from_response=_docking_command_error_from_response, **kwargs)
    + +
    [docs] def docking_command_full_async(self, station_id, clock_identifier, end_time, + prep_pose_behavior=None, lease=None, **kwargs): + """Identical to docking_command_async(), except will return the full DockingCommandResponse.""" + req = self._docking_command_request(lease, station_id, clock_identifier, end_time, + prep_pose_behavior) + return self.call_async(self._stub.DockingCommand, req, + error_from_response=_docking_command_error_from_response, **kwargs)
    + + +
    [docs] def docking_command_feedback_full(self, command_id, **kwargs): + """Check the status of a previously issued docking command. + + Args: + command_id: The ID returned from a previous docking_command call. + Raises: + RpcError: problem communicating with the robot + + Returns: + DockingCommandFeedbackResponse + """ + req = self._docking_command_feedback_request(command_id) + return self.call(self._stub.DockingCommandFeedback, req, + error_from_response=common_header_errors, **kwargs)
    + +
    [docs] def docking_command_feedback_full_async(self, command_id, **kwargs): + """Async version of docking_command_feedback_full().""" + req = self._docking_command_feedback_request(command_id) + return self.call_async(self._stub.DockingCommandFeedback, req, + error_from_response=common_header_errors, **kwargs)
    -
    [docs] def docking_command_feedback(self, command_id, **kwargs): +
    [docs] @deprecated( + reason='This function can raise LeaseErrors when the feedback was successfully retrieved. ' + 'Use docking_command_feedback_full instead.', version='3.0.0', action='always') + def docking_command_feedback(self, command_id, **kwargs): """Check the status of a previously issued docking command. Args: command_id: The ID returned from a previous docking_command call. Returns: - Status of type DockingCommandResponse.Status + Status of type DockingCommandFeedbackResponse.Status """ req = self._docking_command_feedback_request(command_id) return self.call(self._stub.DockingCommandFeedback, req, self._docking_status_from_response, _docking_feedback_error_from_response, **kwargs)
    -
    [docs] def docking_command_feedback_async(self, command_id, **kwargs): +
    [docs] @deprecated( + reason='This function can raise LeaseErrors when the feedback was successfully retrieved. ' + 'Use docking_command_feedback_full_async instead.', version='3.0.0', action='always') + def docking_command_feedback_async(self, command_id, **kwargs): """Async version of docking_command_feedback().""" req = self._docking_command_feedback_request(command_id) return self.call_async(self._stub.DockingCommandFeedback, req, @@ -727,7 +816,7 @@

    Source code for bosdyn.client.docking

         return None
     
     
    -
    [docs]def blocking_dock_robot(robot, dock_id, num_retries=4): +
    [docs]def blocking_dock_robot(robot, dock_id, num_retries=4, timeout=30): """Blocking helper that takes control of the robot and docks it. Args: @@ -749,18 +838,21 @@

    Source code for bosdyn.client.docking

         # Try to dock the robot
         while attempt_number < num_retries and not docking_success:
             attempt_number += 1
    -        cmd_end_time = time.time() + 30  # expect to finish in 30 seconds
    +        converter = robot.time_sync.get_robot_time_converter()
    +        start_time = converter.robot_seconds_from_local_seconds(now_sec())
    +        cmd_end_time = start_time + timeout
             cmd_timeout = cmd_end_time + 10  # client side buffer
     
             prep_pose = (docking_pb2.PREP_POSE_USE_POSE if
                          (attempt_number % 2) else docking_pb2.PREP_POSE_SKIP_POSE)
     
    -        cmd_id = docking_client.docking_command(
    -            dock_id, robot.time_sync.endpoint.clock_identifier,
    -            robot.time_sync.robot_timestamp_from_local_secs(cmd_end_time), prep_pose)
    +        cmd_id = docking_client.docking_command(dock_id, robot.time_sync.endpoint.clock_identifier,
    +                                                seconds_to_timestamp(cmd_end_time), prep_pose)
     
    -        while time.time() < cmd_timeout:
    -            status = docking_client.docking_command_feedback(cmd_id)
    +        while converter.robot_seconds_from_local_seconds(now_sec()) < cmd_timeout:
    +            feedback = docking_client.docking_command_feedback_full(cmd_id)
    +            maybe_raise(common_lease_errors(feedback))
    +            status = feedback.status
                 if status == docking_pb2.DockingCommandFeedbackResponse.STATUS_IN_PROGRESS:
                     # keep waiting/trying
                     time.sleep(1)
    @@ -806,17 +898,19 @@ 

    Source code for bosdyn.client.docking

         """
         docking_client = robot.ensure_client(DockingClient.default_service_name)
     
    -    # Try and put the robot in a safe position
    -    cmd_end_time = time.time() + timeout
    +    converter = robot.time_sync.get_robot_time_converter()
    +    start_time = converter.robot_seconds_from_local_seconds(now_sec())
    +    cmd_end_time = start_time + timeout
         cmd_timeout = cmd_end_time + 10  # client side buffer
     
    -    cmd_id = docking_client.docking_command(
    -        dock_id, robot.time_sync.endpoint.clock_identifier,
    -        robot.time_sync.robot_timestamp_from_local_secs(cmd_end_time),
    -        docking_pb2.PREP_POSE_ONLY_POSE)
    +    cmd_id = docking_client.docking_command(dock_id, robot.time_sync.endpoint.clock_identifier,
    +                                            seconds_to_timestamp(cmd_end_time),
    +                                            docking_pb2.PREP_POSE_ONLY_POSE)
     
    -    while time.time() < cmd_timeout:
    -        status = docking_client.docking_command_feedback(cmd_id)
    +    while converter.robot_seconds_from_local_seconds(now_sec()) < cmd_timeout:
    +        feedback = docking_client.docking_command_feedback_full(cmd_id)
    +        maybe_raise(common_lease_errors(feedback))
    +        status = feedback.status
             if status == docking_pb2.DockingCommandFeedbackResponse.STATUS_IN_PROGRESS:
                 # keep waiting/trying
                 time.sleep(1)
    @@ -843,16 +937,19 @@ 

    Source code for bosdyn.client.docking

         """
         docking_client = robot.ensure_client(DockingClient.default_service_name)
     
    -    # Try and put the robot in a safe position
    -    cmd_end_time = time.time() + timeout
    +    converter = robot.time_sync.get_robot_time_converter()
    +    start_time = converter.robot_seconds_from_local_seconds(now_sec())
    +    cmd_end_time = start_time + timeout
         cmd_timeout = cmd_end_time + 10  # client side buffer
     
    -    cmd_id = docking_client.docking_command(
    -        0, robot.time_sync.endpoint.clock_identifier,
    -        robot.time_sync.robot_timestamp_from_local_secs(cmd_end_time), docking_pb2.PREP_POSE_UNDOCK)
    +    cmd_id = docking_client.docking_command(0, robot.time_sync.endpoint.clock_identifier,
    +                                            seconds_to_timestamp(cmd_end_time),
    +                                            docking_pb2.PREP_POSE_UNDOCK)
     
    -    while time.time() < cmd_timeout:
    -        status = docking_client.docking_command_feedback(cmd_id)
    +    while converter.robot_seconds_from_local_seconds(now_sec()) < cmd_timeout:
    +        feedback = docking_client.docking_command_feedback_full(cmd_id)
    +        maybe_raise(common_lease_errors(feedback))
    +        status = feedback.status
             if status == docking_pb2.DockingCommandFeedbackResponse.STATUS_IN_PROGRESS:
                 # keep waiting/trying
                 time.sleep(1)
    diff --git a/docs/html/_modules/bosdyn/client/door.html b/docs/html/_modules/bosdyn/client/door.html
    index f5a2a9f4b..f42584b8a 100644
    --- a/docs/html/_modules/bosdyn/client/door.html
    +++ b/docs/html/_modules/bosdyn/client/door.html
    @@ -7,7 +7,7 @@
       
       
       
    -  bosdyn.client.door — Spot 2.3.5 documentation
    +  bosdyn.client.door — Spot 3.0.0 documentation
       
     
       
    @@ -66,7 +66,7 @@
                 
                 
                   
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/client/estop.html b/docs/html/_modules/bosdyn/client/estop.html index eb1048e7d..8ef7ecd37 100644 --- a/docs/html/_modules/bosdyn/client/estop.html +++ b/docs/html/_modules/bosdyn/client/estop.html @@ -7,7 +7,7 @@ - bosdyn.client.estop — Spot 2.3.5 documentation + bosdyn.client.estop — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -962,11 +1003,10 @@

    Source code for bosdyn.client.estop

             else:
                 cpt_seconds = int(self.estop_cut_power_timeout)
                 cpt_nanos = int((self.estop_cut_power_timeout - cpt_seconds) * 1e9)
    -            return estop_pb2.EstopEndpoint(role=self.role, name=self._name,
    -                                           unique_id=self._unique_id,
    -                                           timeout=Duration(seconds=t_seconds, nanos=t_nanos),
    -                                           cut_power_timeout=Duration(seconds=cpt_seconds,
    -                                                                      nanos=cpt_nanos))
    + return estop_pb2.EstopEndpoint( + role=self.role, name=self._name, unique_id=self._unique_id, + timeout=Duration(seconds=t_seconds, nanos=t_nanos), + cut_power_timeout=Duration(seconds=cpt_seconds, nanos=cpt_nanos))
    def _response(self): """Generate a response for self._challenge.""" @@ -1123,8 +1163,9 @@

    Source code for bosdyn.client.estop

                     self._error('RPC took longer than {:.2f} seconds'.format(self._rpc_timeout),
                                 exception=exc)
                 except RpcError as exc:
    -                self._error('Transport exception during check-in:\n{}\n'
    -                            '    (resuming check-in)'.format(exc), exception=exc)
    +                self._error(
    +                    'Transport exception during check-in:\n{}\n'
    +                    '    (resuming check-in)'.format(exc), exception=exc)
                 except EndpointUnknownError as exc:
                     # Disable ourself to show we cannot estop any longer.
                     self._error(str(exc), exception=exc, disable=True)
    @@ -1164,6 +1205,7 @@ 

    Source code for bosdyn.client.estop

             ERROR = 1
             DISABLED = 2
    +
    [docs]def is_estopped(estop_client, **kwargs): """Returns true if robot is estopped, false otherwise. @@ -1173,6 +1215,7 @@

    Source code for bosdyn.client.estop

         response = estop_client.get_status(**kwargs)
         return response.stop_level != estop_pb2.ESTOP_LEVEL_NONE
    +
    [docs]def response_from_challenge(challenge): return ctypes.c_ulonglong(~challenge).value
    @@ -1180,10 +1223,10 @@

    Source code for bosdyn.client.estop

     _CHECK_IN_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
     _CHECK_IN_STATUS_TO_ERROR.update({
         estop_pb2.EstopCheckInResponse.STATUS_OK: (None, None),
    -    estop_pb2.EstopCheckInResponse.STATUS_ENDPOINT_UNKNOWN: (EndpointUnknownError,
    -                                                             EndpointUnknownError.__doc__),
    +    estop_pb2.EstopCheckInResponse.STATUS_ENDPOINT_UNKNOWN:
    +        (EndpointUnknownError, EndpointUnknownError.__doc__),
         estop_pb2.EstopCheckInResponse.STATUS_INCORRECT_CHALLENGE_RESPONSE:
    -    (IncorrectChallengeResponseError, IncorrectChallengeResponseError.__doc__),
    +        (IncorrectChallengeResponseError, IncorrectChallengeResponseError.__doc__),
     })
     
     
    @@ -1224,9 +1267,11 @@ 

    Source code for bosdyn.client.estop

     _DEREGISTER_ENDPOINT_STATUS_TO_ERROR.update({
         estop_pb2.DeregisterEstopEndpointResponse.STATUS_SUCCESS: (None, None),
         estop_pb2.DeregisterEstopEndpointResponse.STATUS_ENDPOINT_MISMATCH:
    -    (EndpointMismatchError, EndpointMismatchError.__doc__),
    -    estop_pb2.DeregisterEstopEndpointResponse.STATUS_CONFIG_MISMATCH: (ConfigMismatchError,
    -                                                                       ConfigMismatchError.__doc__),
    +        (EndpointMismatchError, EndpointMismatchError.__doc__),
    +    estop_pb2.DeregisterEstopEndpointResponse.STATUS_CONFIG_MISMATCH:
    +        (ConfigMismatchError, ConfigMismatchError.__doc__),
    +    estop_pb2.DeregisterEstopEndpointResponse.STATUS_MOTORS_ON:
    +        (MotorsOnError, MotorsOnError.__doc__),
     })
     
     
    @@ -1243,11 +1288,11 @@ 

    Source code for bosdyn.client.estop

     _REGISTER_ENDPOINT_STATUS_TO_ERROR.update({
         estop_pb2.RegisterEstopEndpointResponse.STATUS_SUCCESS: (None, None),
         estop_pb2.RegisterEstopEndpointResponse.STATUS_ENDPOINT_MISMATCH:
    -    (EndpointMismatchError, EndpointMismatchError.__doc__),
    -    estop_pb2.RegisterEstopEndpointResponse.STATUS_CONFIG_MISMATCH: (ConfigMismatchError,
    -                                                                     ConfigMismatchError.__doc__),
    -    estop_pb2.RegisterEstopEndpointResponse.STATUS_INVALID_ENDPOINT: (InvalidEndpointError,
    -                                                                      InvalidEndpointError.__doc__),
    +        (EndpointMismatchError, EndpointMismatchError.__doc__),
    +    estop_pb2.RegisterEstopEndpointResponse.STATUS_CONFIG_MISMATCH:
    +        (ConfigMismatchError, ConfigMismatchError.__doc__),
    +    estop_pb2.RegisterEstopEndpointResponse.STATUS_INVALID_ENDPOINT:
    +        (InvalidEndpointError, InvalidEndpointError.__doc__),
     })
     
     
    diff --git a/docs/html/_modules/bosdyn/client/exceptions.html b/docs/html/_modules/bosdyn/client/exceptions.html
    index b4a806cda..1c1204fdf 100644
    --- a/docs/html/_modules/bosdyn/client/exceptions.html
    +++ b/docs/html/_modules/bosdyn/client/exceptions.html
    @@ -7,7 +7,7 @@
       
       
       
    -  bosdyn.client.exceptions — Spot 2.3.5 documentation
    +  bosdyn.client.exceptions — Spot 3.0.0 documentation
       
     
       
    @@ -66,7 +66,7 @@
                 
                 
                   
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -577,16 +618,12 @@

    Source code for bosdyn.client.exceptions

     
     
     
    [docs]class InvalidRequestError(ResponseError): - """The provided request arguments are ill-formed or invalid. - - This is programmer error, hence it should be fixed and not ignored. - This error does not depend on the state of the system."""
    + """The provided request arguments are ill-formed or invalid, independent of the system state."""
    [docs]class LeaseUseError(ResponseError): - """Request was rejected due to using an invalid lease. + """Request was rejected due to using an invalid lease."""
    - This is thrown by services outside of LeaseService."""
    [docs]class LicenseError(ResponseError): """Request was rejected due to using an invalid license."""
    @@ -617,71 +654,83 @@

    Source code for bosdyn.client.exceptions

             return '{}: {}'.format(full_classname, self.error_message)
    -
    [docs]class ClientCancelledOperationError(RpcError): +
    [docs]class RetryableRpcError(RpcError): + """An RpcError that denotes the same request may succeed if retried."""
    + + +
    [docs]class PersistentRpcError(RpcError): + """An RpcError that will almost certainly continue to keep failing if retried"""
    + + +
    [docs]class ClientCancelledOperationError(PersistentRpcError): """The user cancelled the rpc request."""
    -
    [docs]class InvalidAppTokenError(RpcError): +
    [docs]class InvalidAppTokenError(PersistentRpcError): """The provided app token is invalid."""
    -
    [docs]class InvalidClientCertificateError(RpcError): +
    [docs]class InvalidClientCertificateError(PersistentRpcError): """The provided client certificate is invalid."""
    -
    [docs]class NonexistentAuthorityError(RpcError): +
    [docs]class NonexistentAuthorityError(PersistentRpcError): """The app token's authority field names a nonexistent service."""
    -
    [docs]class PermissionDeniedError(RpcError): +
    [docs]class PermissionDeniedError(PersistentRpcError): """The rpc request was denied access."""
    -
    [docs]class ProxyConnectionError(RpcError): +
    [docs]class ProxyConnectionError(RetryableRpcError): """The proxy on the robot could not be reached."""
    -
    [docs]class ResponseTooLargeError(RpcError): +
    [docs]class ResponseTooLargeError(RetryableRpcError): """The rpc response was larger than allowed max size."""
    -
    [docs]class ServiceUnavailableError(RpcError): +
    [docs]class ServiceUnavailableError(RetryableRpcError): """The proxy could not find the (possibly unregistered) service."""
    -
    [docs]class ServiceFailedDuringExecutionError(RpcError): +
    [docs]class TooManyRequestsError(RetryableRpcError): + """The remote procedure call did not go through the proxy due to rate limiting."""
    + + +
    [docs]class ServiceFailedDuringExecutionError(RetryableRpcError): """The service encountered an unexpected failure."""
    -
    [docs]class TimedOutError(RpcError): +
    [docs]class TimedOutError(RetryableRpcError): """The remote procedure call did not terminate within the allotted time."""
    -
    [docs]class UnableToConnectToRobotError(RpcError): +
    [docs]class UnableToConnectToRobotError(RetryableRpcError): """The robot may be offline or otherwise unreachable."""
    [docs]class RetryableUnavailableError(UnableToConnectToRobotError): - """gRPC service unavailable. Likely transient and can be resolved by retrying the request."""
    + """Service unavailable or channel reset. Likely transient and can be resolved by retrying."""
    -
    [docs]class UnauthenticatedError(RpcError): +
    [docs]class UnauthenticatedError(PersistentRpcError): """The user needs to authenticate or does not have permission to access requested service."""
    -
    [docs]class UnknownDnsNameError(RpcError): +
    [docs]class UnknownDnsNameError(PersistentRpcError): """The system is unable to translate the domain name."""
    -
    [docs]class NotFoundError(RpcError): +
    [docs]class NotFoundError(PersistentRpcError): """The backend system could not be found."""
    -
    [docs]class UnimplementedError(RpcError): +
    [docs]class UnimplementedError(PersistentRpcError): """The API does not recognize the request and is unable to complete the request."""
    -
    [docs]class TransientFailureError(RpcError): +
    [docs]class TransientFailureError(RetryableRpcError): """The channel is in state TRANSIENT_FAILURE, often caused by a connection failure."""
    diff --git a/docs/html/_modules/bosdyn/client/fault.html b/docs/html/_modules/bosdyn/client/fault.html index eaec2810c..dec33b8d6 100644 --- a/docs/html/_modules/bosdyn/client/fault.html +++ b/docs/html/_modules/bosdyn/client/fault.html @@ -7,7 +7,7 @@ - bosdyn.client.fault — Spot 2.3.5 documentation + bosdyn.client.fault — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/client/frame_helpers.html b/docs/html/_modules/bosdyn/client/frame_helpers.html index eb4a70686..802ac4832 100644 --- a/docs/html/_modules/bosdyn/client/frame_helpers.html +++ b/docs/html/_modules/bosdyn/client/frame_helpers.html @@ -7,7 +7,7 @@ - bosdyn.client.frame_helpers — Spot 2.3.5 documentation + bosdyn.client.frame_helpers — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -725,7 +766,8 @@

    Source code for bosdyn.client.frame_helpers

         return se3_a_tform_b.get_closest_se2_transform()
    -
    [docs]def express_se2_velocity_in_new_frame(frame_tree_snapshot, frame_b, frame_c, vel_of_a_in_b, validate=True): +
    [docs]def express_se2_velocity_in_new_frame(frame_tree_snapshot, frame_b, frame_c, vel_of_a_in_b, + validate=True): """Convert the SE2 Velocity in frame b to a SE2 Velocity in frame c using the frame tree snapshot. @@ -757,7 +799,9 @@

    Source code for bosdyn.client.frame_helpers

         vel_of_a_in_c = math_helpers.transform_se2velocity(c_adjoint_b, vel_of_a_in_b)
         return vel_of_a_in_c
    -
    [docs]def express_se3_velocity_in_new_frame(frame_tree_snapshot, frame_b, frame_c, vel_of_a_in_b, validate=True): + +
    [docs]def express_se3_velocity_in_new_frame(frame_tree_snapshot, frame_b, frame_c, vel_of_a_in_b, + validate=True): """Convert the SE(3) Velocity in frame b to an SE(3) Velocity in frame c using the frame tree snapshot. @@ -782,6 +826,7 @@

    Source code for bosdyn.client.frame_helpers

         vel_of_a_in_c = math_helpers.transform_se3velocity(c_adjoint_b, vel_of_a_in_b)
         return vel_of_a_in_c
    +
    [docs]def get_odom_tform_body(frame_tree_snapshot): """Get the transformation between "odom" frame and "body" frame from the FrameTreeSnapshot.""" return get_a_tform_b(frame_tree_snapshot, ODOM_FRAME_NAME, BODY_FRAME_NAME)
    diff --git a/docs/html/_modules/bosdyn/client/graph_nav.html b/docs/html/_modules/bosdyn/client/graph_nav.html index 86f9806e1..cbda56385 100644 --- a/docs/html/_modules/bosdyn/client/graph_nav.html +++ b/docs/html/_modules/bosdyn/client/graph_nav.html @@ -7,7 +7,7 @@ - bosdyn.client.graph_nav — Spot 2.3.5 documentation + bosdyn.client.graph_nav — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -556,6 +597,7 @@

    Source code for bosdyn.client.graph_nav

     import collections
     import math
     import os
    +from deprecated import deprecated
     from bosdyn.api.graph_nav import graph_nav_service_pb2_grpc
     from bosdyn.api.graph_nav import graph_nav_service_pb2
     from bosdyn.api.graph_nav import graph_nav_pb2
    @@ -636,14 +678,15 @@ 

    Source code for bosdyn.client.graph_nav

             return self.call_async(self._stub.SetLocalization, req, _localization_from_response,
                                    _set_localization_error, **kwargs)
    -
    [docs] def get_localization_state(self, - request_live_point_cloud=False, - request_live_images=False, - request_live_terrain_maps=False, - request_live_world_objects=False, - request_live_robot_state=False, - waypoint_id=None, - **kwargs): +
    [docs] def get_localization_state( + self, + request_live_point_cloud=False, + request_live_images=False, + request_live_terrain_maps=False, + request_live_world_objects=False, + request_live_robot_state=False, + waypoint_id=None, + **kwargs): """Obtain current localization state of the robot. Returns: @@ -656,8 +699,7 @@

    Source code for bosdyn.client.graph_nav

                 request_live_images=request_live_images,
                 request_live_terrain_maps=request_live_terrain_maps,
                 request_live_world_objects=request_live_world_objects,
    -            request_live_robot_state=request_live_robot_state,
    -            waypoint_id=waypoint_id)
    +            request_live_robot_state=request_live_robot_state, waypoint_id=waypoint_id)
             return self.call(self._stub.GetLocalizationState, req, None, common_header_errors, **kwargs)
    [docs] def get_localization_state_async(self, request_live_point_cloud=False, @@ -674,18 +716,22 @@

    Source code for bosdyn.client.graph_nav

             return self.call_async(self._stub.GetLocalizationState, req, None, common_header_errors,
                                    **kwargs)
    -
    [docs] def navigate_route(self, route, cmd_duration, travel_params=None, leases=None, - timesync_endpoint=None, command_id=None, **kwargs): +
    [docs] def navigate_route(self, route, cmd_duration, route_follow_params=None, travel_params=None, + leases=None, timesync_endpoint=None, command_id=None, + destination_waypoint_tform_body_goal=None, **kwargs): """Navigate the given route. Args: route: Route protobuf of the route to follow. + route_follow_params: What should the robot do if it is not at the expected point in the + route, or the route is blocked. travel_params: API TravelParams for the route. cmd_duration: Number of seconds the command can run for. leases: Leases to show ownership of necessary resources. Will use the client's leases by default. timesync_endpoint: Use this endpoint for timesync fields. Will use the client's endpoint by default. command_id: If not None, this continues an existing navigate_route command with the given ID. If None, a new command_id will be used. + destination_waypoint_tform_body_goal: SE2Pose protobuf of an offset relative to the destination waypoint. kwargs: Passed to underlying RPC. Example: timeout=5 to cancel the RPC after 5 seconds. Returns: Command ID to use in feedback lookup. @@ -697,8 +743,9 @@

    Source code for bosdyn.client.graph_nav

                 graph_nav.TooDistantError: Time too far in the future.
                 graph_nav.RobotImpairedError: Robot cannot travel a route.
                 graph_nav.IsRecordingError: Robot cannot navigate while recording.
    -            graph_nav.UnkownRouteElementsError: Unknown edges or waypoints
    +            graph_nav.UnknownRouteElementsError: Unknown edges or waypoints
                 graph_nav.InvalidEdgeError: Mismatch between edges and waypoints.
    +            graph_nav.NoPathError: No path to the specified route.
                 graph_nav.RobotNotLocalizedToRouteError: The robot is localized somewhere else.
                 graph_nav.ConstraintFaultError: The route involves invalid constraints.
                 graph_nav.RouteNavigationError: A subclass detailing trouble navigating the route.
    @@ -706,23 +753,56 @@ 

    Source code for bosdyn.client.graph_nav

             used_endpoint = timesync_endpoint or self._timesync_endpoint
             if not used_endpoint:
                 raise GraphNavServiceResponseError(response=None, error_message='No timesync endpoint!')
    -        request = self._build_navigate_route_request(route, travel_params, cmd_duration, leases,
    -                                                     used_endpoint, command_id)
    +        request = self._build_navigate_route_request(route, route_follow_params, travel_params,
    +                                                     cmd_duration, leases, used_endpoint,
    +                                                     command_id,
    +                                                     destination_waypoint_tform_body_goal)
             return self.call(self._stub.NavigateRoute, request,
                              _command_id_from_navigate_route_response, _navigate_route_error, **kwargs)
    -
    [docs] def navigate_route_async(self, route, cmd_duration, travel_params=None, leases=None, - timesync_endpoint=None, command_id=None, **kwargs): +
    [docs] def navigate_route_async(self, route, cmd_duration, route_follow_params=None, + travel_params=None, leases=None, timesync_endpoint=None, + command_id=None, destination_waypoint_tform_body_goal=None, **kwargs): """Async version of navigate_route()""" used_endpoint = timesync_endpoint or self._timesync_endpoint if not used_endpoint: raise GraphNavServiceResponseError(response=None, error_message='No timesync endpoint!') - request = self._build_navigate_route_request(route, travel_params, cmd_duration, leases, - used_endpoint, command_id) + request = self._build_navigate_route_request(route, route_follow_params, travel_params, + cmd_duration, leases, used_endpoint, + command_id, + destination_waypoint_tform_body_goal) return self.call_async(self._stub.NavigateRoute, request, _command_id_from_navigate_route_response, _navigate_route_error, **kwargs)
    +
    [docs] def navigate_route_full(self, route, route_follow_params, cmd_duration, travel_params=None, + leases=None, timesync_endpoint=None, command_id=None, + destination_waypoint_tform_body_goal=None, **kwargs): + """Identical to navigate_route(), except will return the full NavigateRouteResponse.""" + used_endpoint = timesync_endpoint or self._timesync_endpoint + if not used_endpoint: + raise GraphNavServiceResponseError(response=None, error_message='No timesync endpoint!') + request = self._build_navigate_route_request(route, route_follow_params, travel_params, + cmd_duration, leases, used_endpoint, command_id, + destination_waypoint_tform_body_goal) + return self.call(self._stub.NavigateRoute, request, + error_from_response=_navigate_route_error, **kwargs)
    + +
    [docs] def navigate_route_full_async(self, route, cmd_duration, route_follow_params=None, + travel_params=None, leases=None, timesync_endpoint=None, + command_id=None, destination_waypoint_tform_body_goal=None, + **kwargs): + """Async version of navigate_route_full().""" + used_endpoint = timesync_endpoint or self._timesync_endpoint + if not used_endpoint: + raise GraphNavServiceResponseError(response=None, error_message='No timesync endpoint!') + request = self._build_navigate_route_request(route, route_follow_params, travel_params, + cmd_duration, leases, used_endpoint, + command_id, + destination_waypoint_tform_body_goal) + return self.call_async(self._stub.NavigateRoute, request, + error_from_response=_navigate_route_error, **kwargs)
    +
    [docs] def navigate_to(self, destination_waypoint_id, cmd_duration, route_params=None, travel_params=None, leases=None, timesync_endpoint=None, command_id=None, destination_waypoint_tform_body_goal=None, **kwargs): @@ -757,8 +837,8 @@

    Source code for bosdyn.client.graph_nav

             if not used_endpoint:
                 raise GraphNavServiceResponseError(response=None, error_message='No timesync endpoint!')
             request = self._build_navigate_to_request(destination_waypoint_id, travel_params,
    -                                                  route_params, cmd_duration, leases, used_endpoint, command_id,
    -                                                  destination_waypoint_tform_body_goal)
    +                                                  route_params, cmd_duration, leases, used_endpoint,
    +                                                  command_id, destination_waypoint_tform_body_goal)
             return self.call(self._stub.NavigateTo, request,
                              value_from_response=_command_id_from_navigate_route_response,
                              error_from_response=_navigate_to_error, **kwargs)
    @@ -771,12 +851,97 @@

    Source code for bosdyn.client.graph_nav

             if not used_endpoint:
                 raise GraphNavServiceResponseError(response=None, error_message='No timesync endpoint!')
             request = self._build_navigate_to_request(destination_waypoint_id, travel_params,
    -                                                  route_params, cmd_duration, leases, used_endpoint, command_id,
    -                                                  destination_waypoint_tform_body_goal)
    +                                                  route_params, cmd_duration, leases, used_endpoint,
    +                                                  command_id, destination_waypoint_tform_body_goal)
             return self.call_async(self._stub.NavigateTo, request,
                                    value_from_response=_command_id_from_navigate_route_response,
                                    error_from_response=_navigate_to_error, **kwargs)
    +
    [docs] def navigate_to_full(self, destination_waypoint_id, cmd_duration, route_params=None, + travel_params=None, leases=None, timesync_endpoint=None, command_id=None, + destination_waypoint_tform_body_goal=None, **kwargs): + """Identical to navigate_to(), except will return the full NavigateToResponse.""" + used_endpoint = timesync_endpoint or self._timesync_endpoint + if not used_endpoint: + raise GraphNavServiceResponseError(response=None, error_message='No timesync endpoint!') + request = self._build_navigate_to_request(destination_waypoint_id, travel_params, + route_params, cmd_duration, leases, used_endpoint, + command_id, destination_waypoint_tform_body_goal) + return self.call(self._stub.NavigateTo, request, + error_from_response=_navigate_to_error, **kwargs)
    + +
    [docs] def navigate_to_full_async(self, destination_waypoint_id, cmd_duration, route_params=None, + travel_params=None, leases=None, timesync_endpoint=None, command_id=None, + destination_waypoint_tform_body_goal=None, **kwargs): + """Async version of navigate_to_full().""" + used_endpoint = timesync_endpoint or self._timesync_endpoint + if not used_endpoint: + raise GraphNavServiceResponseError(response=None, error_message='No timesync endpoint!') + request = self._build_navigate_to_request(destination_waypoint_id, travel_params, + route_params, cmd_duration, leases, used_endpoint, + command_id, destination_waypoint_tform_body_goal) + return self.call_async(self._stub.NavigateTo, request, + error_from_response=_navigate_to_error, **kwargs)
    + +
    [docs] def navigate_to_anchor(self, seed_tform_goal, cmd_duration, route_params=None, + travel_params=None, leases=None, timesync_endpoint=None, + goal_waypoint_rt_seed_ewrt_seed_tolerance=None, command_id=None, + **kwargs): + """Navigate to a pose in seed frame along a route chosen by the GraphNav service. + + Args: + seed_tform_goal: SE3Pose protobuf of the goal pose in seed frame. + cmd_duration: Number of seconds the command can run for. + route_params: API RouteGenParams for the route. + travel_params: API TravelParams for the route. + leases: Leases to show ownership of necessary resources. Will use the client's leases by default. + timesync_endpoint: Use this endpoint for timesync fields. Will use the client's endpoint by default. + goal_waypoint_rt_seed_ewrt_seed_tolerance: Vec3 protobuf of the tolerances for goal waypoint selection. + command_id: If not None, this continues an existing navigate_to command with the given ID. If None, + a new command_id will be used. + Returns: + int: Command ID to use in feedback lookup. + Raises: + RpcError: Problem communicating with the robot. + LeaseUseError: Error using provided leases. + graph_nav.NoTimeSyncError: Missing clock identifier. + graph_nav.CommandExpiredError: Command already expired. + graph_nav.TooDistantError: Time too far in the future. + graph_nav.RobotImpairedError: Robot cannot travel a route. + graph_nav.IsRecordingError: Robot cannot navigate while recording. + graph_nav.NoAnchoringError: There is no anchoring. + graph_nav.NoPathError: No route to goal waypoint, or no goal waypoint found. + graph_nav.InvalidPoseError: The requested pose is invalid, or known to be unachievable. + graph_nav.RobotNotLocalizedToRouteError: The robot not correctly localized. + graph_nav.RouteNavigationError: A subclass detailing trouble navigating the route. + """ + used_endpoint = timesync_endpoint or self._timesync_endpoint + if not used_endpoint: + raise GraphNavServiceResponseError(response=None, error_message='No timesync endpoint!') + request = self._build_navigate_to_anchor_request(seed_tform_goal, travel_params, + route_params, cmd_duration, leases, + used_endpoint, command_id, + goal_waypoint_rt_seed_ewrt_seed_tolerance) + return self.call(self._stub.NavigateToAnchor, request, + value_from_response=_command_id_from_navigate_route_response, + error_from_response=_navigate_to_anchor_error, **kwargs)
    + +
    [docs] def navigate_to_anchor_async(self, seed_tform_goal, cmd_duration, route_params=None, + travel_params=None, leases=None, timesync_endpoint=None, + goal_waypoint_rt_seed_ewrt_seed_tolerance=None, command_id=None, + **kwargs): + """Async version of navigate_to_anchor().""" + used_endpoint = timesync_endpoint or self._timesync_endpoint + if not used_endpoint: + raise GraphNavServiceResponseError(response=None, error_message='No timesync endpoint!') + request = self._build_navigate_to_anchor_request(seed_tform_goal, travel_params, + route_params, cmd_duration, leases, + used_endpoint, command_id, + goal_waypoint_rt_seed_ewrt_seed_tolerance) + return self.call_async(self._stub.NavigateTo, request, + value_from_response=_command_id_from_navigate_route_response, + error_from_response=_navigate_to_anchor_error, **kwargs)
    +
    [docs] def navigation_feedback(self, command_id=0, **kwargs): """Returns the feedback corresponding to the active route follow command. @@ -810,35 +975,38 @@

    Source code for bosdyn.client.graph_nav

             """
             request = self._build_clear_graph_request(lease)
             return self.call(self._stub.ClearGraph, request, value_from_response=None,
    -                         error_from_response=handle_common_header_errors(common_lease_errors), **kwargs)
    + error_from_response=handle_common_header_errors(common_lease_errors), + **kwargs)
    [docs] def clear_graph_async(self, lease=None, **kwargs): """Async version of clear_graph().""" request = self._build_clear_graph_request(lease) return self.call_async(self._stub.ClearGraph, request, value_from_response=None, - error_from_response=handle_common_header_errors(common_lease_errors), **kwargs)
    + error_from_response=handle_common_header_errors(common_lease_errors), + **kwargs)
    -
    [docs] def upload_graph(self, lease=None, graph=None, **kwargs): +
    [docs] def upload_graph(self, lease=None, graph=None, generate_new_anchoring=False, **kwargs): """Uploads a graph to the server and appends to the existing graph. Args: leases: Leases to show ownership of necessary resources. Will use the client's leases by default. graph: Graph protobuf that represents the map with waypoints and edges. + generate_new_anchoring: Whether to generate an (overwrite the) anchoring on upload. Returns: The response, which includes waypoint and edge id's sorted by whether it was cached. Raises: RpcError: Problem communicating with the robot. LeaseUseError: Error using provided lease. """ - request = self._build_upload_graph_request(lease, graph) + request = self._build_upload_graph_request(lease, graph, generate_new_anchoring) return self.call(self._stub.UploadGraph, request, value_from_response=_get_response, - error_from_response=handle_common_header_errors(common_lease_errors), **kwargs)
    + error_from_response=_upload_graph_error, **kwargs)
    -
    [docs] def upload_graph_async(self, lease=None, graph=None, **kwargs): +
    [docs] def upload_graph_async(self, lease=None, graph=None, generate_new_anchoring=False, **kwargs): """Async version of upload_graph().""" - request = self._build_upload_graph_request(lease, graph) + request = self._build_upload_graph_request(lease, graph, generate_new_anchoring) return self.call_async(self._stub.UploadGraph, request, value_from_response=_get_response, - error_from_response=handle_common_header_errors(common_lease_errors), **kwargs)
    + error_from_response=_upload_graph_error, **kwargs)
    [docs] def upload_waypoint_snapshot(self, waypoint_snapshot, lease=None, **kwargs): """Uploads large waypoint snapshot as a stream for a particular waypoint. @@ -853,10 +1021,11 @@

    Source code for bosdyn.client.graph_nav

                 LeaseUseError: Error using provided lease.
             """
             serialized = waypoint_snapshot.SerializeToString()
    -        self.call(self._stub.UploadWaypointSnapshot,
    -                  GraphNavClient._data_chunk_iterator_upload_waypoint_snapshot(
    -                      serialized, lease, self._data_chunk_size), value_from_response=None,
    -                  error_from_response=handle_common_header_errors(common_lease_errors), **kwargs)
    + self.call( + self._stub.UploadWaypointSnapshot, + GraphNavClient._data_chunk_iterator_upload_waypoint_snapshot( + serialized, lease, self._data_chunk_size), value_from_response=None, + error_from_response=handle_common_header_errors(common_lease_errors), **kwargs)
    [docs] def upload_edge_snapshot(self, edge_snapshot, lease=None, **kwargs): """Uploads large edge snapshot as a stream for a particular edge. @@ -871,10 +1040,12 @@

    Source code for bosdyn.client.graph_nav

                 LeaseUseError: Error using provided leases.
             """
             serialized = edge_snapshot.SerializeToString()
    -        self.call(self._stub.UploadEdgeSnapshot,
    -                  GraphNavClient._data_chunk_iterator_upload_edge_snapshot(
    -                      serialized, lease, self._data_chunk_size), value_from_response=None,
    -                  error_from_response=handle_common_header_errors(common_lease_errors), **kwargs)
    + self.call( + self._stub.UploadEdgeSnapshot, + GraphNavClient._data_chunk_iterator_upload_edge_snapshot(serialized, lease, + self._data_chunk_size), + value_from_response=None, + error_from_response=handle_common_header_errors(common_lease_errors), **kwargs)
    [docs] def download_graph(self, **kwargs): """Downloads the graph from the server. @@ -894,28 +1065,33 @@

    Source code for bosdyn.client.graph_nav

             return self.call_async(self._stub.DownloadGraph, request, value_from_response=_get_graph,
                                    error_from_response=common_header_errors, **kwargs)
    -
    [docs] def download_waypoint_snapshot(self, - waypoint_snapshot_id, - download_images=False, - **kwargs): +
    [docs] def download_waypoint_snapshot( + self, + waypoint_snapshot_id, + download_images=False, + do_not_download_point_cloud=False, + **kwargs): """Download a specific waypoint snapshot with streaming from the server. Args: waypoint_snapshot_id: WaypointSnapshot string ID for which snapshot to download from robot. download_images: Boolean indicating whether or not to include images in the download. + do_not_download_point_cloud: Boolean indicating if point cloud data should not be downloaded. Returns: The WaypointSnapshot protobuf from the robot's current map. Raises: RpcError: Problem communicating with the robot UnknownMapInformationError: Snapshot id not found """ - request = self._build_download_waypoint_snapshot_request(waypoint_snapshot_id, - download_images - ) + request = self._build_download_waypoint_snapshot_request( + waypoint_snapshot_id, + download_images, + do_not_download_point_cloud) return self.call(self._stub.DownloadWaypointSnapshot, request, value_from_response=_get_streamed_waypoint_snapshot, error_from_response=_download_waypoint_snapshot_stream_errors, **kwargs)
    +
    [docs] def download_edge_snapshot(self, edge_snapshot_id, **kwargs): """Downloads a specific edge snapshot with streaming from the server. @@ -946,11 +1122,15 @@

    Source code for bosdyn.client.graph_nav

             self._write_bytes(directory, '/graph', graph_bytes)
     
             for waypoint in graph.waypoints:
    +            if len(waypoint.snapshot_id) == 0:
    +                continue
                 waypoint_snapshot = self.download_waypoint_snapshot(waypoint.snapshot_id)
                 self._write_bytes(directory + '/waypoint_snapshots', '/' + waypoint.snapshot_id,
                                   waypoint_snapshot.SerializeToString())
     
             for edge in graph.edges:
    +            if len(edge.snapshot_id) == 0:
    +                continue
                 edge_snapshot = self.download_edge_snapshot(edge.snapshot_id)
                 self._write_bytes(directory + '/edge_snapshots', '/' + edge.snapshot_id,
                                   edge_snapshot.SerializeToString())
    @@ -979,24 +1159,25 @@

    Source code for bosdyn.client.graph_nav

             return request
     
         @staticmethod
    -    def _build_get_localization_state_request(request_live_point_cloud,
    -                                              request_live_images, request_live_terrain_maps,
    -                                              request_live_world_objects, request_live_robot_state,
    -                                              waypoint_id):
    +    def _build_get_localization_state_request(request_live_point_cloud, request_live_images,
    +                                              request_live_terrain_maps, request_live_world_objects,
    +                                              request_live_robot_state, waypoint_id):
             return graph_nav_pb2.GetLocalizationStateRequest(
                 request_live_point_cloud=request_live_point_cloud,
                 request_live_images=request_live_images,
                 request_live_terrain_maps=request_live_terrain_maps,
                 request_live_world_objects=request_live_world_objects,
    -            request_live_robot_state=request_live_robot_state,
    -            waypoint_id=waypoint_id)
    +            request_live_robot_state=request_live_robot_state, waypoint_id=waypoint_id)
     
         @staticmethod
    -    def _build_navigate_route_request(route, travel_params, end_time_secs, leases,
    -                                      timesync_endpoint, command_id):
    +    def _build_navigate_route_request(route, route_follow_params, travel_params, end_time_secs,
    +                                      leases, timesync_endpoint, command_id,
    +                                      destination_waypoint_tform_body_goal):
             converter = timesync_endpoint.get_robot_time_converter()
             request = graph_nav_pb2.NavigateRouteRequest(
    -            route=route, clock_identifier=timesync_endpoint.clock_identifier)
    +            route=route, route_follow_params=route_follow_params,
    +            destination_waypoint_tform_body_goal=destination_waypoint_tform_body_goal,
    +            clock_identifier=timesync_endpoint.clock_identifier)
             if travel_params is not None:
                 request.travel_params.CopyFrom(travel_params)
             request.end_time.CopyFrom(
    @@ -1024,6 +1205,25 @@ 

    Source code for bosdyn.client.graph_nav

                 request.command_id = command_id
             return request
     
    +    @staticmethod
    +    def _build_navigate_to_anchor_request(seed_tform_goal, travel_params, route_params,
    +                                          end_time_secs, leases, timesync_endpoint, command_id,
    +                                          goal_waypoint_rt_seed_ewrt_seed_tolerance):
    +        converter = timesync_endpoint.get_robot_time_converter()
    +        request = graph_nav_pb2.NavigateToAnchorRequest(
    +            seed_tform_goal=seed_tform_goal,
    +            goal_waypoint_rt_seed_ewrt_seed_tolerance=goal_waypoint_rt_seed_ewrt_seed_tolerance,
    +            clock_identifier=timesync_endpoint.clock_identifier)
    +        request.end_time.CopyFrom(
    +            converter.robot_timestamp_from_local_secs(time.time() + end_time_secs))
    +        if travel_params is not None:
    +            request.travel_params.CopyFrom(travel_params)
    +        if route_params is not None:
    +            request.route_params.CopyFrom(route_params)
    +        if command_id is not None:
    +            request.command_id = command_id
    +        return request
    +
         @staticmethod
         def _build_clear_graph_request(lease):
             return graph_nav_pb2.ClearGraphRequest(lease=lease)
    @@ -1033,8 +1233,9 @@ 

    Source code for bosdyn.client.graph_nav

             return graph_nav_pb2.NavigationFeedbackRequest(command_id=command_id)
     
         @staticmethod
    -    def _build_upload_graph_request(lease, graph):
    -        return graph_nav_pb2.UploadGraphRequest(lease=lease, graph=graph)
    +    def _build_upload_graph_request(lease, graph, generate_new_anchoring):
    +        return graph_nav_pb2.UploadGraphRequest(lease=lease, graph=graph,
    +                                                generate_new_anchoring=generate_new_anchoring)
     
         @staticmethod
         def _data_chunk_iterator_upload_waypoint_snapshot(serialized_waypoint_snapshot, lease,
    @@ -1073,11 +1274,16 @@ 

    Source code for bosdyn.client.graph_nav

             return graph_nav_pb2.DownloadGraphRequest()
     
         @staticmethod
    -    def _build_download_waypoint_snapshot_request(waypoint_snapshot_id, download_images
    -                                                  ):
    +    def _build_download_waypoint_snapshot_request(
    +            waypoint_snapshot_id,
    +            download_images,
    +        do_not_download_point_cloud=False
    +    ):
             return graph_nav_pb2.DownloadWaypointSnapshotRequest(
    -            waypoint_snapshot_id=waypoint_snapshot_id, download_images=download_images
    -            )
    +            waypoint_snapshot_id=waypoint_snapshot_id,
    +            download_images=download_images,
    +            do_not_download_point_cloud=do_not_download_point_cloud
    +        )
     
         @staticmethod
         def _build_download_edge_snapshot_request(edge_snapshot_id):
    @@ -1138,6 +1344,18 @@ 

    Source code for bosdyn.client.graph_nav

         """General class of errors for the GraphNav Recording Service."""
    +
    [docs]class UploadGraphError(GraphNavServiceResponseError): + """Errors related to uploading a graph."""
    + + +
    [docs]class MapTooLargeLicenseError(UploadGraphError): + """The map is too large for the license on the robot."""
    + + +
    [docs]class InvalidGraphError(UploadGraphError): + """The graph is invalid topologically, e.g. missing waypoints referenced by edges."""
    + +
    [docs]class RequestAbortedError(GraphNavServiceResponseError): """Request was aborted by the system."""
    @@ -1156,51 +1374,97 @@

    Source code for bosdyn.client.graph_nav

     
     
    [docs]class TimeError(GraphNavServiceResponseError): """Errors associated with timestamps and time sync."""
    + +
    [docs]class CommandExpiredError(TimeError): """The command was received after its end time had already passed."""
    + +
    [docs]class NoTimeSyncError(TimeError): """Client has not performed timesync with robot."""
    + +
    [docs]class TooDistantError(TimeError): """The command was too far in the future."""
    [docs]class RobotStateError(GraphNavServiceResponseError): """Errors associated with the current state of the robot."""
    + +
    [docs]class IsRecordingError(RobotStateError): """Cannot navigate a route while recording a map."""
    + +
    [docs]class RobotImpairedError(RobotStateError): """Robot has a critical perception or behavior fault and cannot navigate."""
    [docs]class RouteError(GraphNavServiceResponseError): """Errors associated with the specified route."""
    + +
    [docs]class ConstraintFaultError(RouteError): """Route parameters contained a constraint fault."""
    + +
    [docs]class InvalidEdgeError(RouteError): """One or more edges do not connect to expected waypoints."""
    -
    [docs]class UnkownRouteElementsError(RouteError): + + +
    [docs]@deprecated(reason='Use UnknownRouteElementsError instead', version='3.0.0', action='ignore') +class UnkownRouteElementsError(RouteError): """One or more waypoints/edges are not in the map."""
    + + +
    [docs]class UnknownRouteElementsError(UnkownRouteElementsError): + """One or more waypoints/edges are not in the map."""
    + +
    [docs]class NoPathError(RouteError): """There is no path to the specified waypoint."""
    + +
    [docs]class UnknownWaypointError(RouteError): """One or more waypoints are not in the map."""
    +
    [docs]class NoAnchoringError(RouteError): + """There is no anchoring."""
    + + +
    [docs]class InvalidPoseError(RouteError): + """The requested pose is invalid, or known to be unachievable."""
    + +
    [docs]class RouteNavigationError(GraphNavServiceResponseError): """Errors related to how the robot navigates the route."""
    + +
    [docs]class FeatureDesertError(RouteNavigationError): """Route contained too many waypoints with low-quality features."""
    + +
    [docs]class RouteNotUpdatingError(RouteNavigationError): """Graph nav was unable to update and follow the specified route."""
    + +
    [docs]class RobotLostError(RouteNavigationError): """Cannot issue a navigation request when the robot is already lost."""
    + +
    [docs]class RobotNotLocalizedToRouteError(RouteNavigationError): """The current localization doesn't refer to any waypoint in the route (possibly uninitialized localization)."""
    + +
    [docs]class RobotStuckError(RouteNavigationError): """The robot is stuck or unable to find a way forward. Resend the command with a new ID, or send a different command to try again."""
    + +
    [docs]class UnrecongizedCommandError(RouteNavigationError): """Happens when you try to continue a command that was either expired, or had an unrecognized id."""
    + def _localization_from_response(response): """Return the localization state from the response.""" return response.localization @@ -1258,6 +1522,24 @@

    Source code for bosdyn.client.graph_nav

         return edge_snapshot
     
     
    +_UPLOAD_GRAPH_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
    +_UPLOAD_GRAPH_STATUS_TO_ERROR.update({
    +    graph_nav_pb2.UploadGraphResponse.STATUS_OK: (None, None),
    +    graph_nav_pb2.UploadGraphResponse.STATUS_MAP_TOO_LARGE_LICENSE: error_pair(MapTooLargeLicenseError),
    +    graph_nav_pb2.UploadGraphResponse.STATUS_INVALID_GRAPH: error_pair(InvalidGraphError),
    +})
    +
    +
    +@handle_common_header_errors
    +@handle_lease_use_result_errors
    +@handle_unset_status_error(unset='STATUS_UNKNOWN')
    +def _upload_graph_error(response):
    +    """Return a custom exception based on upload graph response, None if no error."""
    +    return error_factory(response, response.status,
    +                         status_to_string=graph_nav_pb2.UploadGraphResponse.Status.Name,
    +                         status_to_error=_UPLOAD_GRAPH_STATUS_TO_ERROR)
    +
    +
     _SET_LOCALIZATION_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
     _SET_LOCALIZATION_STATUS_TO_ERROR.update({
         graph_nav_pb2.SetLocalizationResponse.STATUS_OK: (None, None),
    @@ -1294,9 +1576,11 @@ 

    Source code for bosdyn.client.graph_nav

         graph_nav_pb2.NavigateRouteResponse.STATUS_RECORDING:
             error_pair(IsRecordingError),
         graph_nav_pb2.NavigateRouteResponse.STATUS_UNKNOWN_ROUTE_ELEMENTS:
    -        error_pair(UnkownRouteElementsError),
    +        error_pair(UnknownRouteElementsError),
         graph_nav_pb2.NavigateRouteResponse.STATUS_INVALID_EDGE:
             error_pair(InvalidEdgeError),
    +    graph_nav_pb2.NavigateRouteResponse.STATUS_NO_PATH:
    +        error_pair(NoPathError),
         graph_nav_pb2.NavigateRouteResponse.STATUS_CONSTRAINT_FAULT:
             error_pair(ConstraintFaultError),
         graph_nav_pb2.NavigateRouteResponse.STATUS_FEATURE_DESERT:
    @@ -1305,6 +1589,8 @@ 

    Source code for bosdyn.client.graph_nav

             error_pair(RobotLostError),
         graph_nav_pb2.NavigateRouteResponse.STATUS_NOT_LOCALIZED_TO_ROUTE:
             error_pair(RobotNotLocalizedToRouteError),
    +    graph_nav_pb2.NavigateRouteResponse.STATUS_NOT_LOCALIZED_TO_MAP:
    +        error_pair(RobotNotLocalizedToRouteError),
         graph_nav_pb2.NavigateRouteResponse.STATUS_COULD_NOT_UPDATE_ROUTE:
             error_pair(RouteNotUpdatingError),
         graph_nav_pb2.NavigateRouteResponse.STATUS_STUCK:
    @@ -1364,6 +1650,48 @@ 

    Source code for bosdyn.client.graph_nav

                              status_to_error=_NAVIGATE_TO_STATUS_TO_ERROR)
     
     
    +_NAVIGATE_TO_ANCHOR_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
    +_NAVIGATE_TO_ANCHOR_STATUS_TO_ERROR.update({
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_OK: (None, None),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_NO_TIMESYNC:
    +        error_pair(NoTimeSyncError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_EXPIRED:
    +        error_pair(CommandExpiredError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_TOO_DISTANT:
    +        error_pair(TooDistantError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_ROBOT_IMPAIRED:
    +        error_pair(RobotImpairedError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_RECORDING:
    +        error_pair(IsRecordingError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_NO_PATH:
    +        error_pair(NoPathError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_NO_ANCHORING:
    +        error_pair(NoAnchoringError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_FEATURE_DESERT:
    +        error_pair(FeatureDesertError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_LOST:
    +        error_pair(RobotLostError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_NOT_LOCALIZED_TO_MAP:
    +        error_pair(RobotNotLocalizedToRouteError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_COULD_NOT_UPDATE_ROUTE:
    +        error_pair(RouteNotUpdatingError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_STUCK:
    +        error_pair(RobotStuckError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_INVALID_POSE:
    +        error_pair(InvalidPoseError)
    +})
    +
    +
    +@handle_common_header_errors
    +@handle_lease_use_result_errors
    +@handle_unset_status_error(unset='STATUS_UNKNOWN')
    +def _navigate_to_anchor_error(response):
    +    """Return a custom exception based on navigate to anchor response, None if no error."""
    +    return error_factory(response, response.status,
    +                         status_to_string=graph_nav_pb2.NavigateToAnchorResponse.Status.Name,
    +                         status_to_error=_NAVIGATE_TO_ANCHOR_STATUS_TO_ERROR)
    +
    +
     @handle_common_header_errors
     @handle_unset_status_error(unset='STATUS_UNKNOWN')
     def _navigate_feedback_error(response):
    diff --git a/docs/html/_modules/bosdyn/client/image.html b/docs/html/_modules/bosdyn/client/image.html
    index c26c44b28..294fa0f79 100644
    --- a/docs/html/_modules/bosdyn/client/image.html
    +++ b/docs/html/_modules/bosdyn/client/image.html
    @@ -7,7 +7,7 @@
       
       
       
    -  bosdyn.client.image — Spot 2.3.5 documentation
    +  bosdyn.client.image — Spot 3.0.0 documentation
       
     
       
    @@ -66,7 +66,7 @@
                 
                 
                   
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -556,7 +597,8 @@

    Source code for bosdyn.client.image

     import numpy as np
     import collections
     from bosdyn.client.common import BaseClient
    -from bosdyn.client.common import (error_factory, error_pair, common_header_errors, handle_common_header_errors)
    +from bosdyn.client.common import (error_factory, error_pair, common_header_errors,
    +                                  handle_common_header_errors)
     from bosdyn.client.exceptions import ResponseError, UnsetStatusError
     
     from bosdyn.api import image_pb2
    @@ -578,17 +620,24 @@ 

    Source code for bosdyn.client.image

     
    [docs]class ImageDataError(ImageResponseError): """System cannot generate image data for the ImageCapture at this time."""
    +
    [docs]class UnsupportedImageFormatRequestedError(ImageResponseError): """The image service cannot return data in the requested format."""
    + _STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None)) _STATUS_TO_ERROR.update({ image_pb2.ImageResponse.STATUS_OK: (None, None), - image_pb2.ImageResponse.STATUS_UNKNOWN_CAMERA: error_pair(UnknownImageSourceError), - image_pb2.ImageResponse.STATUS_SOURCE_DATA_ERROR: error_pair(SourceDataError), - image_pb2.ImageResponse.STATUS_IMAGE_DATA_ERROR: error_pair(ImageDataError), - image_pb2.ImageResponse.STATUS_UNSUPPORTED_IMAGE_FORMAT_REQUESTED: error_pair(UnsupportedImageFormatRequestedError), - image_pb2.ImageResponse.STATUS_UNKNOWN: error_pair(UnsetStatusError), + image_pb2.ImageResponse.STATUS_UNKNOWN_CAMERA: + error_pair(UnknownImageSourceError), + image_pb2.ImageResponse.STATUS_SOURCE_DATA_ERROR: + error_pair(SourceDataError), + image_pb2.ImageResponse.STATUS_IMAGE_DATA_ERROR: + error_pair(ImageDataError), + image_pb2.ImageResponse.STATUS_UNSUPPORTED_IMAGE_FORMAT_REQUESTED: + error_pair(UnsupportedImageFormatRequestedError), + image_pb2.ImageResponse.STATUS_UNKNOWN: + error_pair(UnsetStatusError), }) @@ -689,8 +738,7 @@

    Source code for bosdyn.client.image

             return image_pb2.ListImageSourcesRequest()
    -
    [docs]def build_image_request(image_source_name, quality_percent=75, - image_format=None): +
    [docs]def build_image_request(image_source_name, quality_percent=75, image_format=None): """Helper function which builds an ImageRequest from an image source name. By default the robot will choose an appropriate format when no image format @@ -727,10 +775,13 @@

    Source code for bosdyn.client.image

             filepath(string): The directory to save the image.
         """
         # Determine the data type to decode the image.
    -    if image_response.shot.image.pixel_format == image_pb2.Image.PIXEL_FORMAT_DEPTH_U16:
    +    if image_response.shot.image.pixel_format in (image_pb2.Image.PIXEL_FORMAT_DEPTH_U16,
    +                                                  image_pb2.Image.PIXEL_FORMAT_GREYSCALE_U16):
             dtype = np.uint16
    +        max_val = 2 ** 16 - 1
         else:
             dtype = np.uint8
    +        max_val = 2 ** 8 - 1
     
         num_channels = 1
         pgm_header_number = 'P5'
    @@ -749,7 +800,7 @@ 

    Source code for bosdyn.client.image

             num_channels = 1
         else:
             print("Unsupported pixel format for PGM/PPM: %s." %
    -            image_pb2.Image.PixelFormat.Name(image_response.shot.image.pixel_format))
    +              image_pb2.Image.PixelFormat.Name(image_response.shot.image.pixel_format))
             return
     
         img = np.frombuffer(image_response.shot.image.data, dtype=dtype)
    @@ -758,7 +809,9 @@ 

    Source code for bosdyn.client.image

         try:
             img = img.reshape((height, width, num_channels))
         except ValueError as err:
    -        print("Cannot convert raw image into expected shape (rows %d, cols %d, color channels %d)." % (height, width, num_channels))
    +        print(
    +            "Cannot convert raw image into expected shape (rows %d, cols %d, color channels %d)." %
    +            (height, width, num_channels))
             print(err)
             return
         if not filename:
    @@ -771,12 +824,13 @@ 

    Source code for bosdyn.client.image

             print("Cannot open file %s. Exception thrown: %s" % (filename, err))
             return
     
    -    max_val = np.amax(img)
    -    pgm_header = pgm_header_number + ' ' + str(width) + ' ' + str(height) + ' ' + str(max_val) + '\n'
    +    pgm_header = pgm_header_number + ' ' + str(width) + ' ' + str(height) + ' ' + str(
    +        max_val) + '\n'
         fd_out.write(pgm_header)
         img.tofile(fd_out)
    -    print('Saved matrix with pixel values from camera "%s" to file "%s".' % (
    -        image_response.source.name, filename))
    + print('Saved matrix with pixel values from camera "%s" to file "%s".' % + (image_response.source.name, filename))
    +
    [docs]def write_image_data(image_response, filename="", filepath="."): """Write image data from image_response to a file. @@ -799,6 +853,7 @@

    Source code for bosdyn.client.image

             print('Failed to save "{}".'.format(image_response.source.name))
             print(err)
    +
    [docs]def save_images_as_files(image_responses, filename="", filepath="."): """Write image responses to files. @@ -823,6 +878,31 @@

    Source code for bosdyn.client.image

             else:
                 # Save jpeg format as a jpeg image.
                 write_image_data(image, save_file_name, filepath)
    + +
    [docs]def pixel_to_camera_space(image_proto, pixel_x, pixel_y, depth=1.0): + """Using the camera intrinsics, determine the (x,y,z) point in the camera frame for + the (u,v) pixel coordinates. + + Args: + image_proto (image_pb2.Image): The image in which the pixel coordinates are from + pixel_x (int): x-coordinate. + pixel_y (int): y-coordinate. + depth (double): The depth from the camera to the point of interest. + + Returns: + An (x,y,z) tuple representing the pixel point of interest now described as a point + in the camera frame. + """ + focal_x = image_proto.source.pinhole.intrinsics.focal_length.x + principal_x = image_proto.source.pinhole.intrinsics.principal_point.x + + focal_y = image_proto.source.pinhole.intrinsics.focal_length.y + principal_y = image_proto.source.pinhole.intrinsics.principal_point.y + + x_rt_camera = depth * (pixel_x - principal_x) / focal_x + y_rt_camera = depth * (pixel_y - principal_y) / focal_y + return (x_rt_camera, y_rt_camera, depth)
    +
    diff --git a/docs/html/_modules/bosdyn/client/image_service_helpers.html b/docs/html/_modules/bosdyn/client/image_service_helpers.html index 647b272a3..ad68af5eb 100644 --- a/docs/html/_modules/bosdyn/client/image_service_helpers.html +++ b/docs/html/_modules/bosdyn/client/image_service_helpers.html @@ -7,7 +7,7 @@ - bosdyn.client.image_service_helpers — Spot 2.3.5 documentation + bosdyn.client.image_service_helpers — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -560,7 +601,8 @@

    Source code for bosdyn.client.image_service_helpers

    import bosdyn.util from bosdyn.util import seconds_to_duration, sec_to_nsec from bosdyn.client.fault import FaultClient, ServiceFaultDoesNotExistError -from bosdyn.client.util import populate_response_header, setup_logging +from bosdyn.client.server_util import populate_response_header +from bosdyn.client.util import setup_logging from bosdyn.api import service_fault_pb2 from bosdyn.api import header_pb2 @@ -1099,8 +1141,7 @@

    Source code for bosdyn.client.image_service_helpers

    def __del__(self): for source in self.image_sources_mapped.values(): - source.stop_capturing() - super().__del__()
    + source.stop_capturing()
    diff --git a/docs/html/_modules/bosdyn/client/ir_enable_disable.html b/docs/html/_modules/bosdyn/client/ir_enable_disable.html new file mode 100644 index 000000000..65567c682 --- /dev/null +++ b/docs/html/_modules/bosdyn/client/ir_enable_disable.html @@ -0,0 +1,690 @@ + + + + + + + + + + bosdyn.client.ir_enable_disable — Spot 3.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + + + +
    + +
    + + + + + + + + + + + + + + + + + +
    + +
      + +
    • »
    • + +
    • Module code »
    • + +
    • bosdyn.client.ir_enable_disable
    • + + +
    • + +
    • + +
    + + +
    +
    +
    +
    + +

    Source code for bosdyn.client.ir_enable_disable

    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""A client for the ir-enable-disable service."""
    +from bosdyn.client.common import common_header_errors
    +from bosdyn.client.common import BaseClient
    +from bosdyn.api import ir_enable_disable_pb2
    +from bosdyn.api import ir_enable_disable_service_pb2_grpc
    +
    +
    +
    [docs]class IREnableDisableServiceClient(BaseClient): + """Client to enable and/or disable the robot's IR light emitters in the body and hand sensors.""" + + # Name of the service in the robot's directory listing. + default_service_name = 'ir-enable-disable-service' + # gRPC service proto definition implemented by this service + service_type = 'bosdyn.api.IREnableDisableService' + + def __init__(self): + super(IREnableDisableServiceClient, + self).__init__(ir_enable_disable_service_pb2_grpc.IREnableDisableServiceStub) + +
    [docs] def set_ir_enabled(self, enable, **kwargs): + """ Enable and/or disable the robot's IR light emitters. + + Args: + enable (bool): Whether or not to enable the emitters. + """ + if enable: + request = ir_enable_disable_pb2.IREnableDisableRequest.REQUEST_ON + else: + request = ir_enable_disable_pb2.IREnableDisableRequest.REQUEST_OFF + return self.call(self._stub.IREnableDisable, + ir_enable_disable_pb2.IREnableDisableRequest(request=request), + error_from_response=common_header_errors, **kwargs)
    + +
    [docs] def set_ir_enabled_async(self, enable, **kwargs): + """Async version of set_ir_enabled()""" + if enable: + request = ir_enable_disable_pb2.IREnableDisableRequest.REQUEST_ON + else: + request = ir_enable_disable_pb2.IREnableDisableRequest.REQUEST_OFF + return self.call_async(self._stub.IREnableDisable, + ir_enable_disable_pb2.IREnableDisableRequest(request=request), + error_from_response=common_header_errors, **kwargs)
    +
    + +
    + +
    + +
    +
    + +
    + +
    + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/html/_modules/bosdyn/client/lease.html b/docs/html/_modules/bosdyn/client/lease.html index b7cbb54ba..5e33fdbdd 100644 --- a/docs/html/_modules/bosdyn/client/lease.html +++ b/docs/html/_modules/bosdyn/client/lease.html @@ -7,7 +7,7 @@ - bosdyn.client.lease — Spot 2.3.5 documentation + bosdyn.client.lease — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -556,18 +597,16 @@

    Source code for bosdyn.client.lease

     import collections
     import enum
     import logging
    +from bosdyn.api import lease_pb2
    +import six
     import threading
     import time
     
    +from bosdyn.api.lease_pb2 import AcquireLeaseRequest, AcquireLeaseResponse
     from bosdyn.api.lease_pb2 import Lease as LeaseProto
    -from bosdyn.api.lease_pb2 import AcquireLeaseRequest
    -from bosdyn.api.lease_pb2 import AcquireLeaseResponse
    -from bosdyn.api.lease_pb2 import ListLeasesRequest
    -from bosdyn.api.lease_pb2 import RetainLeaseRequest
    -from bosdyn.api.lease_pb2 import ReturnLeaseRequest
    -from bosdyn.api.lease_pb2 import ReturnLeaseResponse
    -from bosdyn.api.lease_pb2 import TakeLeaseRequest
    -from bosdyn.api.lease_pb2 import TakeLeaseResponse
    +from bosdyn.api.lease_pb2 import (LeaseUseResult, ListLeasesRequest, RetainLeaseRequest,
    +                                  ReturnLeaseRequest, ReturnLeaseResponse, TakeLeaseRequest,
    +                                  TakeLeaseResponse)
     from bosdyn.api.lease_service_pb2_grpc import LeaseServiceStub
     
     from . import common
    @@ -620,6 +659,7 @@ 

    Source code for bosdyn.client.lease

     
     
    [docs]class NoSuchLease(Error): """The requested lease does not exist.""" + def __init__(self, resource): self.resource = resource @@ -629,6 +669,7 @@

    Source code for bosdyn.client.lease

     
     
    [docs]class LeaseNotOwnedByWallet(Error): """The lease is not owned by the wallet.""" + def __init__(self, resource, lease_state): self.resource = resource self.lease_state = lease_state @@ -644,30 +685,30 @@

    Source code for bosdyn.client.lease

     _ACQUIRE_LEASE_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
     _ACQUIRE_LEASE_STATUS_TO_ERROR.update({
         AcquireLeaseResponse.STATUS_OK: (None, None),
    -    AcquireLeaseResponse.STATUS_RESOURCE_ALREADY_CLAIMED: (ResourceAlreadyClaimedError,
    -                                                           ResourceAlreadyClaimedError.__doc__),
    -    AcquireLeaseResponse.STATUS_INVALID_RESOURCE: (InvalidResourceError,
    -                                                   InvalidResourceError.__doc__),
    -    AcquireLeaseResponse.STATUS_NOT_AUTHORITATIVE_SERVICE: (NotAuthoritativeServiceError,
    -                                                            NotAuthoritativeServiceError.__doc__),
    +    AcquireLeaseResponse.STATUS_RESOURCE_ALREADY_CLAIMED:
    +        (ResourceAlreadyClaimedError, ResourceAlreadyClaimedError.__doc__),
    +    AcquireLeaseResponse.STATUS_INVALID_RESOURCE:
    +        (InvalidResourceError, InvalidResourceError.__doc__),
    +    AcquireLeaseResponse.STATUS_NOT_AUTHORITATIVE_SERVICE:
    +        (NotAuthoritativeServiceError, NotAuthoritativeServiceError.__doc__),
     })
     
     _TAKE_LEASE_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
     _TAKE_LEASE_STATUS_TO_ERROR.update({
         TakeLeaseResponse.STATUS_OK: (None, None),
         TakeLeaseResponse.STATUS_INVALID_RESOURCE: (InvalidResourceError, InvalidResourceError.__doc__),
    -    TakeLeaseResponse.STATUS_NOT_AUTHORITATIVE_SERVICE: (NotAuthoritativeServiceError,
    -                                                         NotAuthoritativeServiceError.__doc__),
    +    TakeLeaseResponse.STATUS_NOT_AUTHORITATIVE_SERVICE:
    +        (NotAuthoritativeServiceError, NotAuthoritativeServiceError.__doc__),
     })
     
     _RETURN_LEASE_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
     _RETURN_LEASE_STATUS_TO_ERROR.update({
         ReturnLeaseResponse.STATUS_OK: (None, None),
    -    ReturnLeaseResponse.STATUS_INVALID_RESOURCE: (InvalidResourceError,
    -                                                  InvalidResourceError.__doc__),
    +    ReturnLeaseResponse.STATUS_INVALID_RESOURCE:
    +        (InvalidResourceError, InvalidResourceError.__doc__),
         ReturnLeaseResponse.STATUS_NOT_ACTIVE_LEASE: (NotActiveLeaseError, NotActiveLeaseError.__doc__),
    -    ReturnLeaseResponse.STATUS_NOT_AUTHORITATIVE_SERVICE: (NotAuthoritativeServiceError,
    -                                                           NotAuthoritativeServiceError.__doc__),
    +    ReturnLeaseResponse.STATUS_NOT_AUTHORITATIVE_SERVICE:
    +        (NotAuthoritativeServiceError, NotAuthoritativeServiceError.__doc__),
     })
     
     
    @@ -682,13 +723,13 @@ 

    Source code for bosdyn.client.lease

             lease_proto: bosdyn.api.Lease protobuf object.
         """
     
    -    def __init__(self, lease_proto):
    +    def __init__(self, lease_proto, ignore_is_valid_check=False):
             """Initializes a Lease object.
     
             Raises:
                 ValueError if lease_proto is not present or valid.
             """
    -        if not self.is_valid_proto(lease_proto):
    +        if not ignore_is_valid_check and not self.is_valid_proto(lease_proto):
                 raise ValueError('invalid lease_proto: {}'.format(lease_proto))
             self.lease_proto = lease_proto
     
    @@ -702,7 +743,7 @@ 

    Source code for bosdyn.client.lease

             DIFFERENT_RESOURCES = 6
             DIFFERENT_EPOCHS = 7
    -
    [docs] def compare(self, other_lease): +
    [docs] def compare(self, other_lease, ignore_resources=False): """Compare two different lease objects. Args: @@ -723,10 +764,10 @@

    Source code for bosdyn.client.lease

                 * CompareResult.DIFFERENT_EPOCHS if this lease is for a different
                   epoch than other_lease. There is no way to compare recency/time of
                   Leases for two different epochs.
    -            * CompareResult.INVALID if either this or other_lease is invalid.
             """
             # Sequences are only valid for leases with the same resource and epoch.
    -        if not (self.lease_proto.resource == other_lease.lease_proto.resource):
    +        if not (self.lease_proto.resource
    +                == other_lease.lease_proto.resource) and not ignore_resources:
                 return self.CompareResult.DIFFERENT_RESOURCES
             if not (self.lease_proto.epoch == other_lease.lease_proto.epoch):
                 return self.CompareResult.DIFFERENT_EPOCHS
    @@ -783,6 +824,9 @@ 

    Source code for bosdyn.client.lease

                 sub_lease_proto.client_names.append(client_name)
             return Lease(sub_lease_proto)
    +
    [docs] def is_valid_lease(self): + return Lease.is_valid_proto(self.lease_proto)
    +
    [docs] @staticmethod def is_valid_proto(lease_proto): """Checks whether this lease is valid. @@ -790,7 +834,44 @@

    Source code for bosdyn.client.lease

             Returns:
                bool indicating that this lease has a valid resource and sequence.
             """
    -        return lease_proto and lease_proto.resource and lease_proto.sequence
    + return lease_proto and lease_proto.resource and lease_proto.sequence
    + +
    [docs] @staticmethod + def compare_result_to_lease_use_result_status(compare_result, allow_super_leases): + """Determines the comparable LeaseUseResult.Status enum value based on the CompareResult enum. + + Args: + allow_super_leases(boolean): If true, a super lease will still be considered as "ok"/ + newer when compared to the active lease. + + Raises: + bosdyn.client.lease.Error: Raised if there is an unknown compare result enum value. + + Returns: + The corresponding LeaseUseResult.Status enum value. + """ + if compare_result == Lease.CompareResult.DIFFERENT_EPOCHS: + return LeaseUseResult.STATUS_WRONG_EPOCH + elif compare_result == Lease.CompareResult.DIFFERENT_RESOURCES: + # if the incoming lease's resource doesn't match the active lease resource, + # then mark it as unmanaged. + return LeaseUseResult.STATUS_UNMANAGED + elif compare_result == Lease.CompareResult.SUPER_LEASE: + # In some cases we may want to allow a super lease, so check an optional boolean + # to see if that is the case. + if allow_super_leases: + return LeaseUseResult.STATUS_OK + # In the normal case, a super lease is considered older. + return LeaseUseResult.STATUS_OLDER + elif compare_result == Lease.CompareResult.OLDER: + return LeaseUseResult.STATUS_OLDER + elif (compare_result == Lease.CompareResult.SAME or + compare_result == Lease.CompareResult.SUB_LEASE or + compare_result == Lease.CompareResult.NEWER): + return LeaseUseResult.STATUS_OK + else: + # Shouldn't hit here. The above set of checks should be exhaustive. + raise Error("The comparison result of the leases is unknown/unaccounted for.")
    [docs]class LeaseState(object): @@ -809,7 +890,8 @@

    Source code for bosdyn.client.lease

         STATUS_OTHER_OWNER = Status.OTHER_OWNER
         STATUS_NOT_MANAGED = Status.NOT_MANAGED
     
    -    def __init__(self, lease_status, lease_owner=None, lease=None, lease_current=None, client_name=None):
    +    def __init__(self, lease_status, lease_owner=None, lease=None, lease_current=None,
    +                 client_name=None):
             """
             Args:
                 lease_status(LeaseState.Status): The ownership status of the lease.
    @@ -889,8 +971,12 @@ 

    Source code for bosdyn.client.lease

                 lease: Lease to add in the wallet.
             """
             with self._lock:
    -            self._lease_state_map[lease.lease_proto.resource] = LeaseState(
    -                LeaseState.Status.SELF_OWNER, lease=lease, client_name=self.client_name)
    + self._add_lease_locked(lease)
    + + def _add_lease_locked(self, lease, current=False): + resource = lease.lease_proto.resource + self._lease_state_map[resource] = LeaseState(LeaseState.Status.SELF_OWNER, lease=lease, + client_name=self.client_name)
    [docs] def remove(self, lease): """Remove lease from the wallet. @@ -1066,7 +1152,7 @@

    Source code for bosdyn.client.lease

             """Return an acquired lease.
     
             Args:
    -            lease: Lease to return.
    +            lease (Lease object): Lease to return. This should be a Lease class object, and not the proto.
     
             Raises:
                 InvalidResourceError: Resource is not known to the LeaseService.
    @@ -1105,6 +1191,7 @@ 

    Source code for bosdyn.client.lease

             return self.call_async(self._stub.RetainLease, req, None, common.common_lease_errors,
                                    **kwargs)
    +
    [docs] def list_leases(self, include_full_lease_info=False, **kwargs): """Get a list of the leases. @@ -1130,6 +1217,32 @@

    Source code for bosdyn.client.lease

             return self.call_async(self._stub.ListLeases, req, self._list_leases_success,
                                    common.common_header_errors, **kwargs)
    +
    [docs] def list_leases_full(self, include_full_lease_info=False, **kwargs): + """Get a list of the leases. + + Args: + include_full_lease_info: Whether the returned list of LeaseResources should include + all of the available information about the last lease used. + Defaults to False. + + Returns: + The complete ListLeasesResponse message. + + Raises: + InternalServerError: Service experienced an unexpected error state. + LeaseUseError: Request was rejected due to using an invalid lease. + """ + req = self._make_list_leases_request(include_full_lease_info) + return self.call(self._stub.ListLeases, req, None, + common.common_header_errors, **kwargs)
    + +
    [docs] def list_leases_full_async(self, include_full_lease_info=False, **kwargs): + """Async version of the list_leases() function.""" + req = self._make_list_leases_request(include_full_lease_info) + return self.call_async(self._stub.ListLeases, req, None, + common.common_header_errors, **kwargs)
    + + @staticmethod def _make_acquire_request(resource): """Return AcquireLeaseRequest message with the given resource.""" @@ -1322,11 +1435,20 @@

    Source code for bosdyn.client.lease

                     False if the UI thread is wedged, which prevents the
                     application from continuing to keep the Lease alive when it is
                     no longer in a good state.
    +        on_failure_callback: If specified, this should be a callable function object
    +                which takes the error/exception as an input. The  function does not
    +                need to return anything. This function can be used to action on any
    +                failures during the keepalive from the RetainLease RPC.
    +        warnings: Boolean used to determine if the _periodic_check_in function will print lease check-in errors.
         """
     
         def __init__(self, lease_client, lease_wallet=None, resource=_RESOURCE_BODY,
    -                 rpc_interval_seconds=2, keep_running_cb=None):
    +                 rpc_interval_seconds=2, keep_running_cb=None, host_name="",
    +                 on_failure_callback=None, warnings=True, return_at_exit=False):
             """Create a new LeaseKeepAlive object."""
    +        self.host_name = host_name
    +        self.print_warnings = warnings
    +        self._return_at_exit = return_at_exit
             if not lease_client:
                 raise ValueError("lease_client must be set")
             self._lease_client = lease_client
    @@ -1351,6 +1473,9 @@ 

    Source code for bosdyn.client.lease

     
             self._end_check_in_signal = threading.Event()
     
    +        # If the on_failure_callback is not provided, then set the default as a no-op function.
    +        self._retain_lease_failed_cb = on_failure_callback or (lambda err: None)
    +
             # Configure the thread to do check-ins, and begin checking in.
             self._thread = threading.Thread(target=self._periodic_check_in)
             self._thread.daemon = True
    @@ -1393,6 +1518,8 @@ 

    Source code for bosdyn.client.lease

     
         def __exit__(self, exc_type, exc_val, exc_tb):
             self.shutdown()
    +        if self._return_at_exit:
    +            self._lease_client.return_lease(self.lease_wallet.get_lease(self._resource))
     
         def _ok(self):
             self.logger.debug('Check-in successful')
    @@ -1421,8 +1548,11 @@ 

    Source code for bosdyn.client.lease

                 # We really do want to catch anything.
                 #pylint: disable=broad-except
                 except Exception as exc:
    -                self.logger.warning('Generic exception during check-in:\n%s\n'
    -                                    '    (resuming check-in)', exc)
    +                if self.print_warnings:
    +                    self.logger.warning(
    +                        'Generic exception for %s during check-in:\n%s\n'
    +                        '    (resuming check-in)', self.host_name, exc)
    +                self._retain_lease_failed_cb(exc)
                 else:
                     # No errors!
                     self._ok()
    @@ -1437,6 +1567,73 @@ 

    Source code for bosdyn.client.lease

                 if self._end_check_in_signal.wait(self._rpc_interval_seconds - exec_seconds):
                     break
             self.logger.info('Lease check-in stopped')
    + + +
    [docs]def test_active_lease(incoming_lease_proto, active_lease, sublease_name=None, + allow_super_leases=False): + """Check if an incoming lease is newer than the current lease. + + Args: + incoming_lease_proto(lease_pb2.Lease): The incoming lease proto. + active_lease(Lease): A lease object representing the most recent/newest known lease + that the incoming lease should be compared against. + sublease_name(string): If not NoneType, a sublease of the incoming lease will be + created (with sublease_name as the client name) and used to compare to + the active lease. + allow_super_leases(boolean): If true, a super lease will still be considered as "ok"/ + newer when compared to the active lease. + + Returns: + A tuple containing the lease use result from comparing the incoming and active leases, and + then a Lease object made from the incoming lease proto. The lease object will be None if + the incoming lease proto was invalid. The lease object will be a sublease of the incoming + lease proto if sublease_name is not NoneType. + """ + lease_use_result = LeaseUseResult() + lease_use_result.attempted_lease.CopyFrom(incoming_lease_proto) + + # Ensure the incoming lease is valid. If it is valid, create a sublease for the + # incoming lease and test with that. + try: + incoming_lease = Lease(incoming_lease_proto) + if sublease_name is not None: + incoming_lease = incoming_lease.create_sublease(client_name=sublease_name) + except ValueError: + # Invalid lease proto will throw this error. + lease_use_result.status = LeaseUseResult.STATUS_INVALID_LEASE + return lease_use_result, None + + if active_lease is None: + # No active lease means that the incoming lease is good! + # Set the incoming lease proto as the latest known lease. There will be no previous + # lease since this is the first one for the resource. + lease_use_result.latest_known_lease.CopyFrom(incoming_lease.lease_proto) + lease_use_result.status = LeaseUseResult.STATUS_OK + return lease_use_result, incoming_lease + + # Ensure the active lease is also valid. + if not active_lease.is_valid_lease(): + # Raise an exception since the incoming lease is invalid. + raise Error("The active lease object is invalid.") + + # Set the previous lease as the active lease. + lease_use_result.previous_lease.CopyFrom(active_lease.lease_proto) + + # Set the latest known lease as the active lease. This will be overwritten if the incoming + # lease is found to be newer. + lease_use_result.latest_known_lease.CopyFrom(active_lease.lease_proto) + + # Compare the incoming lease's sublease to the latest known/most recent lease (active lease). + compare_result = incoming_lease.compare(active_lease) + lease_use_result.status = Lease.compare_result_to_lease_use_result_status( + compare_result, allow_super_leases) + if lease_use_result.status == LeaseUseResult.STATUS_OK and compare_result != Lease.CompareResult.SUPER_LEASE: + # Only update the latest known lease if the incoming lease was found as status ok (newer/the same + # as the existing lease). Also prevents a "super-lease" from getting set as the + # latest known lease (when the allow_super_lease boolean is True) when there have been newer + # subleases seen. + lease_use_result.latest_known_lease.CopyFrom(incoming_lease.lease_proto) + return lease_use_result, incoming_lease
    diff --git a/docs/html/_modules/bosdyn/client/license.html b/docs/html/_modules/bosdyn/client/license.html index f69605834..4aa6f5875 100644 --- a/docs/html/_modules/bosdyn/client/license.html +++ b/docs/html/_modules/bosdyn/client/license.html @@ -7,7 +7,7 @@ - bosdyn.client.license — Spot 2.3.5 documentation + bosdyn.client.license — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -556,9 +597,11 @@

    Source code for bosdyn.client.license

     from bosdyn.api import license_pb2, license_service_pb2_grpc
     from bosdyn.client.common import BaseClient, common_header_errors
     
    +
     def _get_entry_value(response):
         return response.license
     
    +
     
    [docs]class LicenseClient(BaseClient): """Client to acquire robot license.""" # Typical name of the service in the robot's directory listing. diff --git a/docs/html/_modules/bosdyn/client/local_grid.html b/docs/html/_modules/bosdyn/client/local_grid.html index d4ab10159..2a92546f8 100644 --- a/docs/html/_modules/bosdyn/client/local_grid.html +++ b/docs/html/_modules/bosdyn/client/local_grid.html @@ -7,7 +7,7 @@ - bosdyn.client.local_grid — Spot 2.3.5 documentation + bosdyn.client.local_grid — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -583,7 +624,8 @@

    Source code for bosdyn.client.local_grid

     
     
    [docs] def get_local_grid_types_async(self, **kwargs): """Async version of get_local_grid_types().""" - return self.call_async(self._stub.GetLocalGridTypes, local_grid_pb2.GetLocalGridTypesRequest(), + return self.call_async(self._stub.GetLocalGridTypes, + local_grid_pb2.GetLocalGridTypesRequest(), value_from_response=lambda res: res.local_grid_type, error_from_response=common_header_errors, **kwargs)
    @@ -607,7 +649,6 @@

    Source code for bosdyn.client.local_grid

                              value_from_response=lambda res: res.local_grid_responses,
                              error_from_response=common_header_errors, **kwargs)
    -
    [docs] def get_local_grids_async(self, local_grid_type_names, **kwargs): """Async version of get_local_grids().""" request = local_grid_pb2.GetLocalGridsRequest() diff --git a/docs/html/_modules/bosdyn/client/log_annotation.html b/docs/html/_modules/bosdyn/client/log_annotation.html index 6546b5654..070ee872e 100644 --- a/docs/html/_modules/bosdyn/client/log_annotation.html +++ b/docs/html/_modules/bosdyn/client/log_annotation.html @@ -7,7 +7,7 @@ - bosdyn.client.log_annotation — Spot 2.3.5 documentation + bosdyn.client.log_annotation — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -577,13 +618,13 @@

    Source code for bosdyn.client.log_annotation

    import bosdyn.api.log_annotation_pb2 as log_annotation_protos
     import bosdyn.api.log_annotation_service_pb2_grpc as log_annotation_service
     
    +
     
    [docs]class InvalidArgument(Error): """A given argument could not be used."""
    -
    [docs]@deprecated( - reason='The log-annotation client and service have been replaced by data_buffer.', - version='2.1.0', - action="always") + +
    [docs]@deprecated(reason='The log-annotation client and service have been replaced by data_buffer.', + version='2.1.0', action="always") class LogAnnotationClient(BaseClient): """A client for adding annotations to robot logs.""" @@ -703,19 +744,17 @@

    Source code for bosdyn.client.log_annotation

    converter = self._timesync_endpoint.get_robot_time_converter()
                 except time_sync.NotEstablishedError:
                     # No timesync. That's OK -- the receiving host will provide the timestamp.
    -                self.logger.debug('Could not timestamp message of type %s',
    -                                  (msg_type if msg_type is not None
    -                                   else (proto.DESCRIPTOR.full_name if proto is not None
    -                                         else 'Unknown')))
    +                self.logger.debug(
    +                    'Could not timestamp message of type %s',
    +                    (msg_type if msg_type is not None else
    +                     (proto.DESCRIPTOR.full_name if proto is not None else 'Unknown')))
                 else:
                     return converter.robot_timestamp_from_local_secs(time.time())
             return None
    -
    [docs]@deprecated( - reason='The log-annotation client and service have been replaced by data_buffer.', - version='2.1.0', - action="always") +
    [docs]@deprecated(reason='The log-annotation client and service have been replaced by data_buffer.', + version='2.1.0', action="always") class LogAnnotationHandler(logging.Handler): """A logging system Handler that will publish text to a bosdyn.api.LogAnnotationService. diff --git a/docs/html/_modules/bosdyn/client/manipulation_api_client.html b/docs/html/_modules/bosdyn/client/manipulation_api_client.html index 9d87ea05b..bcb07081c 100644 --- a/docs/html/_modules/bosdyn/client/manipulation_api_client.html +++ b/docs/html/_modules/bosdyn/client/manipulation_api_client.html @@ -7,7 +7,7 @@ - bosdyn.client.manipulation_api_client — Spot 2.3.5 documentation + bosdyn.client.manipulation_api_client — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -553,12 +594,14 @@

    Source code for bosdyn.client.manipulation_api_client

    """For clients to the Manipulation API service.""" -from bosdyn.client.common import BaseClient +from bosdyn.client.common import (BaseClient, handle_common_header_errors, + handle_lease_use_result_errors) from bosdyn.api import manipulation_api_service_pb2_grpc from .lease import add_lease_wallet_processors +
    [docs]class ManipulationApiClient(BaseClient): """Client for the ManipulationAPI service.""" default_service_name = 'manipulation' @@ -588,7 +631,9 @@

    Source code for bosdyn.client.manipulation_api_client

    Returns: The full ManipulationApiResponse message, which includes a command id for feedback. """ - return self.call(self._stub.ManipulationApi, manipulation_api_request, **kwargs)
    + return self.call(self._stub.ManipulationApi, manipulation_api_request, + error_from_response=_manipulation_api_command_error_from_response, + **kwargs)
    [docs] def manipulation_api_command_async(self, manipulation_api_request, **kwargs): """Async version of the manipulation_api_command().""" @@ -604,11 +649,47 @@

    Source code for bosdyn.client.manipulation_api_client

    Returns: The full ManipulationApiFeedbackResponse message. """ - return self.call(self._stub.ManipulationApiFeedback, manipulation_api_feedback_request, **kwargs)
    + return self.call(self._stub.ManipulationApiFeedback, manipulation_api_feedback_request, + error_from_response=_manipulation_api_feedback_error_from_response, + **kwargs)
    [docs] def manipulation_api_feedback_command_async(self, manipulation_api_feedback_request, **kwargs): """Async version of the manipulation_api_feedback_command().""" - return self.call_async(self._stub.ManipulationApiFeedback, manipulation_api_feedback_request, **kwargs)
    + return self.call_async(self._stub.ManipulationApiFeedback, + manipulation_api_feedback_request, **kwargs)
    + +
    [docs] def grasp_override_command(self, grasp_override_request, **kwargs): + """Issue a grasp override command to the robot. + + Args: + grasp_override_request (manipulation_api_pb2.ApiGraspOverrideRequest): The command request + for a grasp override. + + Returns: + An ApiGraspOverrideResponse message. + """ + return self.call(self._stub.OverrideGrasp, grasp_override_request, + error_from_response=_grasp_override_command_error_from_response, **kwargs)
    + +
    [docs] def grasp_override_command_async(self, grasp_override_request, **kwargs): + """Async version of the grasp_override_command().""" + return self.call_async(self._stub.OverrideGrasp, grasp_override_request, **kwargs)
    + + +@handle_common_header_errors +@handle_lease_use_result_errors +def _manipulation_api_command_error_from_response(response): + return None + + +@handle_common_header_errors +def _manipulation_api_feedback_error_from_response(response): + return None + + +@handle_common_header_errors +def _grasp_override_command_error_from_response(response): + return None
    diff --git a/docs/html/_modules/bosdyn/client/map_processing.html b/docs/html/_modules/bosdyn/client/map_processing.html new file mode 100644 index 000000000..c847f30da --- /dev/null +++ b/docs/html/_modules/bosdyn/client/map_processing.html @@ -0,0 +1,857 @@ + + + + + + + + + + bosdyn.client.map_processing — Spot 3.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + + + +
    + +
    + + + + + + + + + + + + + + + + + +
    + +
      + +
    • »
    • + +
    • Module code »
    • + +
    • bosdyn.client.map_processing
    • + + +
    • + +
    • + +
    + + +
    +
    +
    +
    + +

    Source code for bosdyn.client.map_processing

    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""For clients of the graph_nav map processing service."""
    +from __future__ import print_function
    +import collections
    +from bosdyn.api.graph_nav import map_pb2
    +from bosdyn.api.graph_nav import map_processing_pb2
    +from bosdyn.api.graph_nav import map_processing_service_pb2
    +from bosdyn.api.graph_nav import map_processing_service_pb2_grpc as map_processing
    +from bosdyn.client.common import BaseClient
    +from bosdyn.client.common import (BaseClient, error_factory, handle_unset_status_error,
    +                                  handle_common_header_errors, handle_lease_use_result_errors,
    +                                  common_header_errors)
    +from enum import Enum
    +from bosdyn.client.exceptions import ResponseError
    +
    +
    +
    [docs]class MapProcessingServiceResponseError(ResponseError): + """General class of errors for the GraphNav map processing service."""
    + + +
    [docs]class MissingSnapshotsError(MapProcessingServiceResponseError): + """The uploaded map has missing waypoint snapshots."""
    + + +
    [docs]class OptimizationFailureError(MapProcessingServiceResponseError): + """The anchoring optimization failed."""
    + + +
    [docs]class InvalidGraphError(MapProcessingServiceResponseError): + """The graph is invalid topologically, for example containing missing waypoints referenced by edges."""
    + + +
    [docs]class InvalidParamsError(MapProcessingServiceResponseError): + """The parameters passed to the optimizer do not make sense (e.g negative weights)."""
    + + +
    [docs]class MaxIterationsError(MapProcessingServiceResponseError): + """The optimizer reached the maximum number of iterations before converging."""
    + + +
    [docs]class MaxTimeError(MapProcessingServiceResponseError): + """The optimizer timed out before converging."""
    + + +
    [docs]class InvalidHintsError(MapProcessingServiceResponseError): + """One or more of the hints passed in to the optimizer are invalid (do not correspond to real waypoints or objects)."""
    + + +
    [docs]class ConstraintViolationError(MapProcessingServiceResponseError): + """One or more anchors were moved outside of the desired constraints."""
    + +
    [docs]class MapModifiedError(MapProcessingServiceResponseError): + """The map was modified on the server by another client during processing. Please try again."""
    + + +@handle_common_header_errors +@handle_unset_status_error(unset='STATUS_UNKNOWN') +def _process_topology_common_errors(response): + # Handle error statuses from the request. + if (response.status == map_processing_pb2.ProcessTopologyResponse.STATUS_INVALID_GRAPH): + return InvalidGraphError(response=response, error_message=InvalidGraphError.__doc__) + elif (response.status == + map_processing_pb2.ProcessTopologyResponse.STATUS_MISSING_WAYPOINT_SNAPSHOTS): + return MissingSnapshotsError(response=response, error_message=MissingSnapshotsError.__doc__) + elif (response.status == + map_processing_pb2.ProcessTopologyResponse.STATUS_MAP_MODIFIED_DURING_PROCESSING): + return MapModifiedError(response=response, error_message=MapModifiedError.__doc__) + return None + + +def _process_topology_streamed_errors(responses): + """Return a custom exception based on process topology streaming response, None if no error.""" + # Iterate through the response since the request responds with a stream. + for resp in responses: + exception = _process_topology_common_errors(resp) + if exception: + return exception + + # All responses (in the iterator) had status_ok + return None + + +__ANCHORING_COMMON_ERRORS = { + map_processing_pb2.ProcessAnchoringResponse.STATUS_MISSING_WAYPOINT_SNAPSHOTS: + MissingSnapshotsError, + map_processing_pb2.ProcessAnchoringResponse.STATUS_OPTIMIZATION_FAILURE: + OptimizationFailureError, + map_processing_pb2.ProcessAnchoringResponse.STATUS_INVALID_GRAPH: + InvalidGraphError, + map_processing_pb2.ProcessAnchoringResponse.STATUS_INVALID_PARAMS: + InvalidParamsError, + map_processing_pb2.ProcessAnchoringResponse.STATUS_CONSTRAINT_VIOLATION: + ConstraintViolationError, + map_processing_pb2.ProcessAnchoringResponse.STATUS_MAX_ITERATIONS: + MaxIterationsError, + map_processing_pb2.ProcessAnchoringResponse.STATUS_MAX_TIME: + MaxTimeError, + map_processing_pb2.ProcessAnchoringResponse.STATUS_INVALID_HINTS: + InvalidHintsError, + map_processing_pb2.ProcessAnchoringResponse.STATUS_MAP_MODIFIED_DURING_PROCESSING: + MapModifiedError +} + + +@handle_common_header_errors +@handle_unset_status_error(unset='STATUS_UNKNOWN') +def _process_anchoring_common_errors(response): + # Handle error statuses from the request. + if response.status in __ANCHORING_COMMON_ERRORS: + type_name = __ANCHORING_COMMON_ERRORS[response.status] + return type_name(response=response, error_message=type_name.__doc__) + return None + + +def _process_anchoring_streamed_errors(responses): + """Return a custom exception based on process anchoring streaming response, None if no error.""" + # Iterate through the response since the request responds with a stream. + for resp in responses: + exception = _process_anchoring_common_errors(resp) + if exception: + return exception + + # All responses (in the iterator) had status_ok + return None + + +def _get_streamed_topology_response(response): + """Reads a streamed response to recreate a merged topology response.""" + merged_response = map_processing_pb2.ProcessTopologyResponse() + + for resp in response: + merged_response.MergeFrom(resp) + return merged_response + + +def _get_streamed_anchoring_response(response): + """Reads a streamed response to recreate a merged anchoring response.""" + merged_response = map_processing_pb2.ProcessAnchoringResponse() + + for resp in response: + merged_response.MergeFrom(resp) + return merged_response + + +
    [docs]class MapProcessingServiceClient(BaseClient): + """Client for the GraphNav map processing service.""" + default_service_name = 'map-processing-service' + service_type = 'bosdyn.api.graph_nav.MapProcessingService' + + def __init__(self): + super(MapProcessingServiceClient, self).__init__(map_processing.MapProcessingServiceStub) + + @staticmethod + def _build_process_topology_request(params, modify_map_on_server): + return map_processing_pb2.ProcessTopologyRequest(params=params, + modify_map_on_server=modify_map_on_server) + + @staticmethod + def _build_process_anchoring_request(params, modify_anchoring_on_server, + stream_intermediate_results, initial_hint): + return map_processing_pb2.ProcessAnchoringRequest( + params=params, initial_hint=initial_hint, + modify_anchoring_on_server=modify_anchoring_on_server, + stream_intermediate_results=stream_intermediate_results) + +
    [docs] def process_topology(self, params, modify_map_on_server, **kwargs): + """Process the topology of the map on the server, closing loops and producing a + consistent topology. + + Args: + params: a ProcessTopologyRequest.Params object + modify_map_on_server: if true, the map will be modified on the server. If false, + the subgraph returned by this function should be uploaded back to the server if it + is to be reused. + + Returns: + The ProcessTopologyResponse containing new edges to add to the map. + + Raises: + RpcError: Problem communicating with the robot + """ + request = self._build_process_topology_request(params, modify_map_on_server) + return self.call(self._stub.ProcessTopology, request, + value_from_response=_get_streamed_topology_response, + error_from_response=_process_topology_streamed_errors, **kwargs)
    + +
    [docs] def process_anchoring(self, params, modify_anchoring_on_server, stream_intermediate_results, + initial_hint=None, **kwargs): + """Process the anchoring of the map on the server, producing a metrically consistent anchoring. + + Args: + params: a ProcessAnchoringRequest.Params object + modify_anchoring_on_server: if true, the map will be modified on the server. If false, + the anchoring returned by this function should be uploaded back to the server if it + is to be reused. + stream_intermediate_results: if true, anchorings from earlier optimizer + iterations may be included in the response. If false, only the last iteration will be returned. + initial_hint: Initial guess at some number of waypoints and world objects and their anchorings. + This field is an AnchoringHint object (see map_processing.proto) + Returns: + The ProcessAnchoringResponse containing a new optimized anchoring. + Raises: + RpcError: Problem communicating with the robot + """ + request = self._build_process_anchoring_request(params, modify_anchoring_on_server, + stream_intermediate_results, initial_hint) + + return self.call(self._stub.ProcessAnchoring, request, + value_from_response=_get_streamed_anchoring_response, + error_from_response=_process_anchoring_streamed_errors, **kwargs)
    +
    + +
    + +
    + +
    +
    + +
    + +
    + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/html/_modules/bosdyn/client/math_helpers.html b/docs/html/_modules/bosdyn/client/math_helpers.html index e5296869b..02e48f373 100644 --- a/docs/html/_modules/bosdyn/client/math_helpers.html +++ b/docs/html/_modules/bosdyn/client/math_helpers.html @@ -7,7 +7,7 @@ - bosdyn.client.math_helpers — Spot 2.3.5 documentation + bosdyn.client.math_helpers — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -565,6 +606,7 @@

    Source code for bosdyn.client.math_helpers

             v += 2 * math.pi
         return v
    +
    [docs]def angle_diff_degrees(a1, a2): v = a1 - a2 while v > 180: @@ -573,6 +615,7 @@

    Source code for bosdyn.client.math_helpers

             v += 360
         return v
    +
    [docs]class SE2Pose(object): """Class representing an SE2Pose with position and angle.""" @@ -611,8 +654,8 @@

    Source code for bosdyn.client.math_helpers

     
     
    [docs] def to_proto(self): """Converts the math_helpers.SE2Pose into an output of the protobuf geometry_pb2.SE2Pose.""" - return geometry_pb2.SE2Pose( - position=geometry_pb2.Vec2(x=self.x, y=self.y), angle=self.angle)
    + return geometry_pb2.SE2Pose(position=geometry_pb2.Vec2(x=self.x, y=self.y), + angle=self.angle)
    [docs] def inverse(self): """ @@ -706,13 +749,14 @@

    Source code for bosdyn.client.math_helpers

             # the correct height is passed into the function as height_z.
             return SE3Pose(x=self.x, y=self.y, z=height_z, rot=Quat.from_yaw(self.angle))
    +
    [docs]class SE2Velocity(object): """Class representing an SE2Velocity with linear velocity and angular velocity.""" def __init__(self, x, y, angular): - self.linear_velocity_x = x - self.linear_velocity_y = y - self.angular_velocity = angular + self.linear_velocity_x = float(x) + self.linear_velocity_y = float(y) + self.angular_velocity = float(angular) def __str__(self): return 'Linear velocity -- X: %0.3f Y: %0.3f Angular velocity -- %0.3f ' % ( @@ -733,7 +777,8 @@

    Source code for bosdyn.client.math_helpers

     
    [docs] def to_vector(self): """Creates a 3x1 velocity vector as a numpy array.""" # Create a 3x1 vector of [x_d, y_d, r_d] - return numpy.array([self.linear_velocity_x, self.linear_velocity_y, self.angular_velocity]).reshape((3,1))
    + return numpy.array([self.linear_velocity_x, self.linear_velocity_y, + self.angular_velocity]).reshape((3, 1))
    [docs] @staticmethod def from_vector(se2_vel_vector): @@ -741,12 +786,12 @@

    Source code for bosdyn.client.math_helpers

             if type(se2_vel_vector) == list:
                 if len(se2_vel_vector) != 3:
                     # Must have 3 elements to be a complete SE2Velocity
    -                print(
    -                    "Velocity list must have 3 elements. The input has the wrong dimension of: "
    -                    + str(len(se2_vel_vector)))
    +                print("Velocity list must have 3 elements. The input has the wrong dimension of: " +
    +                      str(len(se2_vel_vector)))
                     return None
                 else:
    -                return SE2Velocity(x=se2_vel_vector[0], y=se2_vel_vector[1], angular=se2_vel_vector[2])
    +                return SE2Velocity(x=se2_vel_vector[0], y=se2_vel_vector[1],
    +                                   angular=se2_vel_vector[2])
             if type(se2_vel_vector) == numpy.ndarray:
                 if se2_vel_vector.shape[0] != 3:
                     # Must have 3 elements to be a complete SE2Velocity
    @@ -780,12 +825,12 @@ 

    Source code for bosdyn.client.math_helpers

         """Class representing an SE3Velocity with linear velocity and angular velocity."""
     
         def __init__(self, lin_x, lin_y, lin_z, ang_x, ang_y, ang_z):
    -        self.linear_velocity_x = lin_x
    -        self.linear_velocity_y = lin_y
    -        self.linear_velocity_z = lin_z
    -        self.angular_velocity_x = ang_x
    -        self.angular_velocity_y = ang_y
    -        self.angular_velocity_z = ang_z
    +        self.linear_velocity_x = float(lin_x)
    +        self.linear_velocity_y = float(lin_y)
    +        self.linear_velocity_z = float(lin_z)
    +        self.angular_velocity_x = float(ang_x)
    +        self.angular_velocity_y = float(ang_y)
    +        self.angular_velocity_z = float(ang_z)
     
         def __str__(self):
             return 'Linear velocity -- X: %0.3f Y: %0.3f Z: %0.3f Angular velocity -- X: %0.3f Y: %0.3f Z: %0.3f' % (
    @@ -815,7 +860,7 @@ 

    Source code for bosdyn.client.math_helpers

             return numpy.array([
                 self.linear_velocity_x, self.linear_velocity_y, self.linear_velocity_z,
                 self.angular_velocity_x, self.angular_velocity_y, self.angular_velocity_z
    -        ]).reshape((6,1))
    + ]).reshape((6, 1))
    @property def linear(self): @@ -843,9 +888,8 @@

    Source code for bosdyn.client.math_helpers

             if type(se3_vel_vector) == list:
                 if len(se3_vel_vector) != 6:
                     # Must have 6 elements to be a complete SE3Velocity
    -                print(
    -                    "Velocity list must have 6 elements. The input has the wrong dimension of: "
    -                    + str(len(se3_vel_vector)))
    +                print("Velocity list must have 6 elements. The input has the wrong dimension of: " +
    +                      str(len(se3_vel_vector)))
                     return None
                 else:
                     return SE3Velocity(lin_x=se3_vel_vector[0], lin_y=se3_vel_vector[1],
    @@ -1021,6 +1065,7 @@ 

    Source code for bosdyn.client.math_helpers

             rot = Quat.from_matrix(mat[0:3, 0:3])
             return SE3Pose(x, y, z, rot)
    +
    [docs] @staticmethod def from_identity(): """Create a math_helpers.SE3Pose representing the identity SE(3) pose.""" @@ -1044,7 +1089,7 @@

    Source code for bosdyn.client.math_helpers

             a_R_b = self.rot.to_matrix()
             position_skew_mat = skew_matrix_3d(self.position)
             mat = numpy.matmul(position_skew_mat, a_R_b)
    -        a_adjoint_b = numpy.block([[a_R_b, mat], [numpy.zeros((3,3)), a_R_b]])
    +        a_adjoint_b = numpy.block([[a_R_b, mat], [numpy.zeros((3, 3)), a_R_b]])
             return a_adjoint_b
    [docs] def get_closest_se2_transform(self): @@ -1053,7 +1098,25 @@

    Source code for bosdyn.client.math_helpers

             # aligned frame, either "vision", "odom", or "flat_body" to be safely converted from
             # an SE3Pose to an SE2Pose with minimal loss of height information.
             se2_angle = (self.rot.closest_yaw_only_quaternion()).to_yaw()
    -        return SE2Pose(x=self.x, y=self.y, angle=se2_angle)
    + return SE2Pose(x=self.x, y=self.y, angle=se2_angle)
    + +
    [docs] @staticmethod + def interp(a, b, fraction): + """ + Performs a blend of two SE3Poses. Out = a * (1 - fraction) + b * fraction + + Args: + a(SE3Pose): Lower blend input. + b(SE3Pose): Upper blend input. + fraction(float): The blending factor. Should be inside [0, 1] + Returns: + SE3Pose + """ + x = a.x * (1.0 - fraction) + b.x * fraction + y = a.y * (1.0 - fraction) + b.y * fraction + z = a.z * (1.0 - fraction) + b.z * fraction + rot = Quat.slerp(a.rot, b.rot, fraction) + return SE3Pose(x, y, z, rot)
    [docs]class Quat(object): @@ -1138,25 +1201,22 @@

    Source code for bosdyn.client.math_helpers

         def _from_matrix_x(rot):
             """Computes the value of the quaternion for the x-axis from a numpy 3x3 rotation matrix."""
             x = math.sqrt(1 + rot[0, 0] - rot[1, 1] - rot[2, 2]) * 0.5
    -        return Quat(
    -            w=(rot[2, 1] - rot[1, 2]) / (4.0 * x), x=x, y=(rot[0, 1] + rot[1, 0]) / (4.0 * x),
    -            z=(rot[0, 2] + rot[2, 0]) / (4.0 * x))
    +        return Quat(w=(rot[2, 1] - rot[1, 2]) / (4.0 * x), x=x,
    +                    y=(rot[0, 1] + rot[1, 0]) / (4.0 * x), z=(rot[0, 2] + rot[2, 0]) / (4.0 * x))
     
         @staticmethod
         def _from_matrix_y(rot):
             """Computes the value of the quaternion for the y-axis from a numpy 3x3 rotation matrix."""
             y = math.sqrt(1 - rot[0, 0] + rot[1, 1] - rot[2, 2]) * 0.5
    -        return Quat(
    -            w=(rot[0, 2] - rot[2, 0]) / (4.0 * y), x=(rot[0, 1] + rot[1, 0]) / (4.0 * y), y=y,
    -            z=(rot[1, 2] + rot[2, 1]) / (4.0 * y))
    +        return Quat(w=(rot[0, 2] - rot[2, 0]) / (4.0 * y), x=(rot[0, 1] + rot[1, 0]) / (4.0 * y),
    +                    y=y, z=(rot[1, 2] + rot[2, 1]) / (4.0 * y))
     
         @staticmethod
         def _from_matrix_z(rot):
             """Computes the value of the quaternion for the z-axis from a numpy 3x3 rotation matrix."""
             z = math.sqrt(1 - rot[0, 0] - rot[1, 1] + rot[2, 2]) * 0.5
    -        return Quat(
    -            w=(rot[1, 0] - rot[0, 1]) / (4.0 * z), x=(rot[0, 2] + rot[2, 0]) / (4.0 * z),
    -            y=(rot[1, 2] + rot[2, 1]) / (4.0 * z), z=z)
    +        return Quat(w=(rot[1, 0] - rot[0, 1]) / (4.0 * z), x=(rot[0, 2] + rot[2, 0]) / (4.0 * z),
    +                    y=(rot[1, 2] + rot[2, 1]) / (4.0 * z), z=z)
     
     
    [docs] @staticmethod def from_roll(angle): @@ -1232,17 +1292,23 @@

    Source code for bosdyn.client.math_helpers

     
     
    [docs] def mult(self, other_quat): """Computes the multiplication of two math_helpers.Quats.""" - return Quat(self.w * other_quat.w - self.x * other_quat.x - self.y * other_quat.y - self.z * other_quat.z, - self.w * other_quat.x + self.x * other_quat.w + self.y * other_quat.z - self.z * other_quat.y, - self.w * other_quat.y - self.x * other_quat.z + self.y * other_quat.w + self.z * other_quat.x, - self.w * other_quat.z + self.x * other_quat.y - self.y * other_quat.x + self.z * other_quat.w)
    + return Quat( + self.w * other_quat.w - self.x * other_quat.x - self.y * other_quat.y - + self.z * other_quat.z, self.w * other_quat.x + self.x * other_quat.w + + self.y * other_quat.z - self.z * other_quat.y, self.w * other_quat.y - + self.x * other_quat.z + self.y * other_quat.w + self.z * other_quat.x, + self.w * other_quat.z + self.x * other_quat.y - self.y * other_quat.x + + self.z * other_quat.w)
    def __mul__(self, other_quat): """Overrides the '*' symbol to compute the multiplication between two math_helpers.Quats.""" - return Quat(self.w * other_quat.w - self.x * other_quat.x - self.y * other_quat.y - self.z * other_quat.z, - self.w * other_quat.x + self.x * other_quat.w + self.y * other_quat.z - self.z * other_quat.y, - self.w * other_quat.y - self.x * other_quat.z + self.y * other_quat.w + self.z * other_quat.x, - self.w * other_quat.z + self.x * other_quat.y - self.y * other_quat.x + self.z * other_quat.w) + return Quat( + self.w * other_quat.w - self.x * other_quat.x - self.y * other_quat.y - + self.z * other_quat.z, self.w * other_quat.x + self.x * other_quat.w + + self.y * other_quat.z - self.z * other_quat.y, self.w * other_quat.y - + self.x * other_quat.z + self.y * other_quat.w + self.z * other_quat.x, + self.w * other_quat.z + self.x * other_quat.y - self.y * other_quat.x + + self.z * other_quat.w)
    [docs] def normalize(self): """Normalizes the quaternion.""" @@ -1262,11 +1328,44 @@

    Source code for bosdyn.client.math_helpers

             """Computes a yaw-only math_helpers.Quat from the current roll/pitch/yaw math_helpers.Quat"""
             mag = math.sqrt(self.w * self.w + self.z * self.z)
             if mag > 0:
    -            return Quat(w=self.w/mag, x=0, y=0, z=self.z/mag)
    +            return Quat(w=self.w / mag, x=0, y=0, z=self.z / mag)
             else:
                 # If the problem is ill posed (i.e. z-axis of quaternion is [0, 0, -1], then preserve old
                 # behavior and always rotate 180 degrees around the y-axis.
    -            return Quat(w=0, x=0, y=1, z=0)
    + return Quat(w=0, x=0, y=1, z=0)
    + +
    [docs] @staticmethod + def slerp(a, b, fraction): + v0 = numpy.array([a.w, a.x, a.y, a.z]) + v1 = numpy.array([b.w, b.x, b.y, b.z]) + dot = numpy.dot(v0.transpose(), v1) + # If the dot product is negative, slerp will not take + # the shorter path. Note that v1 and -v1 are equivalent when + # the negation is applied to all four components. Fix by + # reversing one quaternion. + if dot < 0.0: + v0 *= -1.0 + dot = -dot + + DOT_THRESHOLD = 1.0 - 1e-4 + if dot > DOT_THRESHOLD: + # If the inputs are too close for comfort, linearly interpolate + # and normalize the result. + result = v0 + fraction * (v1 - v0) + result /= numpy.sqrt(numpy.dot(result.transpose(), result)) + else: + # Since dot is in range [0, DOT_THRESHOLD], acos is safe + theta_0 = math.acos(dot) # theta_0 = angle between input vectors + theta = theta_0 * fraction # theta = angle between v0 and result + sin_theta = math.sin(theta) # compute this value only once + sin_theta_0 = math.sin(theta_0) # compute this value only once + + s0 = math.cos( + theta) - dot * sin_theta / sin_theta_0 # == sin(theta_0 - theta) / sin(theta_0) + s1 = sin_theta / sin_theta_0 + + result = (s0 * v0) + (s1 * v1) + return Quat(result[0], result[1], result[2], result[3])
    [docs]def pose_to_xyz_yaw(A_tform_B): @@ -1285,24 +1384,29 @@

    Source code for bosdyn.client.math_helpers

         angle_deg = math.degrees(abs(delta.angle))
         return (dist_2d < max_translation_meters) and (angle_deg < max_yaw_degrees)
    +
    [docs]def recenter_angle(q, lower_limit, upper_limit): recenter_range = upper_limit - lower_limit - while q >= upper_limit: q -= recenter_range - while q < lower_limit: q += recenter_range + while q >= upper_limit: + q -= recenter_range + while q < lower_limit: + q += recenter_range return q
    +
    [docs]def skew_matrix_3d(vec3_proto): """Creates a 3x3 numpy matrix representing the skew symmetric matrix for a vec3. These are used to create the adjoint matrices for SE3Pose's, among other things.""" - return numpy.array([[0, -vec3_proto.z, vec3_proto.y], - [vec3_proto.z, 0, -vec3_proto.x], - [-vec3_proto.y, vec3_proto.x, 0]])
    + return numpy.array([[0, -vec3_proto.z, vec3_proto.y], [vec3_proto.z, 0, -vec3_proto.x], + [-vec3_proto.y, vec3_proto.x, 0]])
    +
    [docs]def skew_matrix_2d(vec2_proto): """Creates a 2x1 numpy matrix representing the skew symmetric matrix for a vec2. These are used to create the adjoint matrices for SE2Pose's, among other things.""" return numpy.array([[vec2_proto.y, -vec2_proto.x]])
    +
    [docs]def transform_se2velocity(a_adjoint_b_matrix, se2_velocity_in_b): """ Changes the frame that the SE(2) Velocity is expressed in. More specifically, it converts the @@ -1321,10 +1425,12 @@

    Source code for bosdyn.client.math_helpers

             se2_velocity_in_b_vector = se2_velocity_in_b.to_vector()
         else:
             return None
    -    se2_velocity_in_a_vector = numpy.asarray(numpy.matmul(a_adjoint_b_matrix, se2_velocity_in_b_vector))
    +    se2_velocity_in_a_vector = numpy.asarray(
    +        numpy.matmul(a_adjoint_b_matrix, se2_velocity_in_b_vector))
         se2_velocity_in_a = SE2Velocity.from_vector(se2_velocity_in_a_vector)
         return se2_velocity_in_a
    +
    [docs]def transform_se3velocity(a_adjoint_b_matrix, se3_velocity_in_b): """ Changes the frame that the SE(3) Velocity is expressed in. More specifically, it converts the diff --git a/docs/html/_modules/bosdyn/client/network_compute_bridge_client.html b/docs/html/_modules/bosdyn/client/network_compute_bridge_client.html index a52d97904..40b17f567 100644 --- a/docs/html/_modules/bosdyn/client/network_compute_bridge_client.html +++ b/docs/html/_modules/bosdyn/client/network_compute_bridge_client.html @@ -7,7 +7,7 @@ - bosdyn.client.network_compute_bridge_client — Spot 2.3.5 documentation + bosdyn.client.network_compute_bridge_client — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -563,15 +604,19 @@

    Source code for bosdyn.client.network_compute_bridge_client

    from bosdyn.api import network_compute_bridge_service_pb2 from bosdyn.api import network_compute_bridge_service_pb2_grpc +
    [docs]class ExternalServiceNotFoundError(ResponseError): """The requested service for external computation was not found in the directory."""
    +
    [docs]class ExternalServerError(ResponseError): """The call to the external server did not complete successfully."""
    +
    [docs]class NetworkComputeRotationError(ResponseError): """The robot failed to rotate the image as requested."""
    +
    [docs]class NetworkComputeBridgeClient(BaseClient): """Client to either the NetworkComputeBridgeService or the NetworkComputeBridgeWorkerService.""" @@ -599,11 +644,13 @@

    Source code for bosdyn.client.network_compute_bridge_client

    ExternalServerError: Either the service or worker service threw an error when responding with the set of all models. """ - return self.call(self._stub.ListAvailableModels, list_request, None, _list_available_models_error, **kwargs)
    + return self.call(self._stub.ListAvailableModels, list_request, None, + _list_available_models_error, **kwargs)
    [docs] def list_available_models_command_async(self, list_request, **kwargs): """Async version of list_available_models_command().""" - return self.call_async(self._stub.ListAvailableModels, list_request, None, _list_available_models_error, **kwargs)
    + return self.call_async(self._stub.ListAvailableModels, list_request, None, + _list_available_models_error, **kwargs)
    [docs] def network_compute_bridge_command(self, network_compute_request, **kwargs): """Issue the main network compute bridge request to run a model on specific, requested data. @@ -625,11 +672,14 @@

    Source code for bosdyn.client.network_compute_bridge_client

    image as requested. """ - return self.call(self._stub.NetworkCompute, network_compute_request, None, _network_compute_error, **kwargs)
    + return self.call(self._stub.NetworkCompute, network_compute_request, None, + _network_compute_error, **kwargs)
    [docs] def network_compute_bridge_command_async(self, network_compute_request, **kwargs): """Async version of network_compute_bridge_command().""" - return self.call_async(self._stub.NetworkCompute, network_compute_request, None, _network_compute_error, **kwargs)
    + return self.call_async(self._stub.NetworkCompute, network_compute_request, None, + _network_compute_error, **kwargs)
    + @handle_common_header_errors def _network_compute_error(response): @@ -641,14 +691,18 @@

    Source code for bosdyn.client.network_compute_bridge_client

    return error_type(response=response, error_message=message) + _NETWORK_COMPUTE_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None)) _NETWORK_COMPUTE_STATUS_TO_ERROR.update({ - network_compute_bridge_pb2.NETWORK_COMPUTE_STATUS_UNKNOWN: error_pair(UnsetStatusError), + network_compute_bridge_pb2.NETWORK_COMPUTE_STATUS_UNKNOWN: + error_pair(UnsetStatusError), network_compute_bridge_pb2.NETWORK_COMPUTE_STATUS_SUCCESS: (None, None), - network_compute_bridge_pb2.NETWORK_COMPUTE_STATUS_EXTERNAL_SERVICE_NOT_FOUND: (ExternalServiceNotFoundError, - ExternalServiceNotFoundError.__doc__), - network_compute_bridge_pb2.NETWORK_COMPUTE_STATUS_EXTERNAL_SERVER_ERROR: (ExternalServerError, None), - network_compute_bridge_pb2.NETWORK_COMPUTE_STATUS_ROTATION_ERROR: (NetworkComputeRotationError, None), + network_compute_bridge_pb2.NETWORK_COMPUTE_STATUS_EXTERNAL_SERVICE_NOT_FOUND: + (ExternalServiceNotFoundError, ExternalServiceNotFoundError.__doc__), + network_compute_bridge_pb2.NETWORK_COMPUTE_STATUS_EXTERNAL_SERVER_ERROR: + (ExternalServerError, None), + network_compute_bridge_pb2.NETWORK_COMPUTE_STATUS_ROTATION_ERROR: + (NetworkComputeRotationError, None), }) @@ -662,13 +716,16 @@

    Source code for bosdyn.client.network_compute_bridge_client

    return error_type(response=response, error_message=message) + _LIST_AVAILABLE_MODELS_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None)) _LIST_AVAILABLE_MODELS_STATUS_TO_ERROR.update({ - network_compute_bridge_pb2.LIST_AVAILABLE_MODELS_STATUS_UNKNOWN: error_pair(UnsetStatusError), + network_compute_bridge_pb2.LIST_AVAILABLE_MODELS_STATUS_UNKNOWN: + error_pair(UnsetStatusError), network_compute_bridge_pb2.LIST_AVAILABLE_MODELS_STATUS_SUCCESS: (None, None), - network_compute_bridge_pb2.LIST_AVAILABLE_MODELS_STATUS_EXTERNAL_SERVICE_NOT_FOUND: (ExternalServiceNotFoundError, - ExternalServiceNotFoundError.__doc__), - network_compute_bridge_pb2.LIST_AVAILABLE_MODELS_STATUS_EXTERNAL_SERVER_ERROR: (ExternalServerError, None), + network_compute_bridge_pb2.LIST_AVAILABLE_MODELS_STATUS_EXTERNAL_SERVICE_NOT_FOUND: + (ExternalServiceNotFoundError, ExternalServiceNotFoundError.__doc__), + network_compute_bridge_pb2.LIST_AVAILABLE_MODELS_STATUS_EXTERNAL_SERVER_ERROR: + (ExternalServerError, None), })
    diff --git a/docs/html/_modules/bosdyn/client/payload.html b/docs/html/_modules/bosdyn/client/payload.html index eaaff9647..0ffafb2d4 100644 --- a/docs/html/_modules/bosdyn/client/payload.html +++ b/docs/html/_modules/bosdyn/client/payload.html @@ -7,7 +7,7 @@ - bosdyn.client.payload — Spot 2.3.5 documentation + bosdyn.client.payload — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/client/payload_registration.html b/docs/html/_modules/bosdyn/client/payload_registration.html index 68343fed4..6ac900c8d 100644 --- a/docs/html/_modules/bosdyn/client/payload_registration.html +++ b/docs/html/_modules/bosdyn/client/payload_registration.html @@ -7,7 +7,7 @@ - bosdyn.client.payload_registration — Spot 2.3.5 documentation + bosdyn.client.payload_registration — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -665,8 +706,12 @@

    Source code for bosdyn.client.payload_registration

    PayloadRegistrationResponseError: Something went wrong during the payload registration. """ request = payload_registration_protos.UpdatePayloadVersionRequest() + # Deprecated credential fields. request.payload_guid = guid request.payload_secret = secret + # Supported credential fields for 2.4+ robots. + request.payload_credentials.guid = guid + request.payload_credentials.secret = secret request.updated_version.CopyFrom(updated_version) return self.call(self._stub.UpdatePayloadVersion, request, error_from_response=_update_payload_version_error, **kw_args)
    @@ -687,11 +732,15 @@

    Source code for bosdyn.client.payload_registration

    PayloadRegistrationResponseError: Something went wrong during the payload registration. """ request = payload_registration_protos.UpdatePayloadVersionRequest() + # Deprecated credential fields. request.payload_guid = guid request.payload_secret = secret + # Supported credential fields for 2.4+ robots. + request.payload_credentials.guid = guid + request.payload_credentials.secret = secret request.updated_version.CopyFrom(updated_version) return self.call_async(self._stub.UpdatePayloadVersion, request, - error_from_response=_update_payload_version_error, **kw_args)
    + error_from_response=_update_payload_version_error, **kw_args)
    [docs] def get_payload_auth_token(self, guid, secret, **kw_args): """Request a limited-access auth token for a payload. @@ -714,10 +763,104 @@

    Source code for bosdyn.client.payload_registration

    does not match any existing payload. PayloadRegistrationResponseError: Something went wrong during the payload registration. """ - request = payload_registration_protos.GetPayloadAuthTokenRequest( - payload_guid=guid, payload_secret=secret) + request = payload_registration_protos.GetPayloadAuthTokenRequest() + # Deprecated credential fields. + request.payload_guid = guid + request.payload_secret = secret + # Supported credential fields for 2.4+ robots. + request.payload_credentials.guid = guid + request.payload_credentials.secret = secret + return self.call(self._stub.GetPayloadAuthToken, request, value_from_response=_get_token, - error_from_response=_get_payload_auth_token_error, **kw_args)
    + error_from_response=_get_payload_auth_token_error, **kw_args)
    + +
    [docs] def attach_payload(self, guid, secret, **kw_args): + """Attach a payload to the robot. + + Args: + guid: The GUID of the payload to attach. + secret: Secret of the payload to attach. + kw_args: Extra arguments to pass to grpc call invocation. + + Raises: + RpcError: Problem communicating with the robot. + PayloadDoesNotExistError: A payload with the provided GUID does not exist. + InvalidPayloadCredentialsError: The GUID + secret does not match an existing payload. + PayloadNotAuthorizedError: The payload you've requested to change is not yet authorized. + PayloadRegistrationResponseError: Something went wrong during the payload registration. + """ + request = payload_registration_protos.UpdatePayloadAttachedRequest() + request.payload_credentials.guid = guid + request.payload_credentials.secret = secret + request.request = payload_registration_protos.UpdatePayloadAttachedRequest.REQUEST_ATTACH + return self.call(self._stub.UpdatePayloadAttached, request, + error_from_response=_update_payload_attached_error, **kw_args)
    + +
    [docs] def attach_payload_async(self, guid, secret, **kw_args): + """Attach a payload to the robot. + + Args: + guid: The GUID of the payload to attach. + secret: Secret of the payload to attach. + kw_args: Extra arguments to pass to grpc call invocation. + + Raises: + RpcError: Problem communicating with the robot. + PayloadDoesNotExistError: A payload with the provided GUID does not exist. + InvalidPayloadCredentialsError: The GUID + secret does not match an existing payload. + PayloadNotAuthorizedError: The payload you've requested to change is not yet authorized. + PayloadRegistrationResponseError: Something went wrong during the payload registration. + """ + request = payload_registration_protos.UpdatePayloadAttachedRequest() + request.payload_credentials.guid = guid + request.payload_credentials.secret = secret + request.request = payload_registration_protos.UpdatePayloadAttachedRequest.REQUEST_ATTACH + return self.call_async(self._stub.UpdatePayloadAttached, request, + error_from_response=_update_payload_attached_error, **kw_args)
    + +
    [docs] def detach_payload(self, guid, secret, **kw_args): + """Detach a payload from the robot. + + Args: + guid: The GUID of the payload to detach. + secret: Secret of the payload to detach. + kw_args: Extra arguments to pass to grpc call invocation. + + Raises: + RpcError: Problem communicating with the robot. + PayloadDoesNotExistError: A payload with the provided GUID does not exist. + InvalidPayloadCredentialsError: The GUID + secret does not match an existing payload. + PayloadNotAuthorizedError: The payload you've requested to change is not yet authorized. + PayloadRegistrationResponseError: Something went wrong during the payload registration. + """ + request = payload_registration_protos.UpdatePayloadAttachedRequest() + request.payload_credentials.guid = guid + request.payload_credentials.secret = secret + request.request = payload_registration_protos.UpdatePayloadAttachedRequest.REQUEST_DETACH + return self.call(self._stub.UpdatePayloadAttached, request, + error_from_response=_update_payload_attached_error, **kw_args)
    + +
    [docs] def detach_payload_async(self, guid, secret, **kw_args): + """Detach a payload from the robot. + + Args: + guid: The GUID of the payload to detach. + secret: Secret of the payload to detach. + kw_args: Extra arguments to pass to grpc call invocation. + + Raises: + RpcError: Problem communicating with the robot. + PayloadDoesNotExistError: A payload with the provided GUID does not exist. + InvalidPayloadCredentialsError: The GUID + secret does not match an existing payload. + PayloadNotAuthorizedError: The payload you've requested to change is not yet authorized. + PayloadRegistrationResponseError: Something went wrong during the payload registration. + """ + request = payload_registration_protos.UpdatePayloadAttachedRequest() + request.payload_credentials.guid = guid + request.payload_credentials.secret = secret + request.request = payload_registration_protos.UpdatePayloadAttachedRequest.REQUEST_DETACH + return self.call_async(self._stub.UpdatePayloadAttached, request, + error_from_response=_update_payload_attached_error, **kw_args)
    # Associate proto status errors to python client errors for RegisterPayload @@ -725,7 +868,7 @@

    Source code for bosdyn.client.payload_registration

    _REGISTER_PAYLOAD_STATUS_TO_ERROR.update({ payload_registration_protos.RegisterPayloadResponse.STATUS_OK: (None, None), payload_registration_protos.RegisterPayloadResponse.STATUS_ALREADY_EXISTS: - (PayloadAlreadyExistsError, PayloadAlreadyExistsError.__doc__), + (PayloadAlreadyExistsError, PayloadAlreadyExistsError.__doc__), }) @@ -745,9 +888,9 @@

    Source code for bosdyn.client.payload_registration

    _UPDATE_PAYLOAD_VERSION_STATUS_TO_ERROR.update({ payload_registration_protos.UpdatePayloadVersionResponse.STATUS_OK: (None, None), payload_registration_protos.UpdatePayloadVersionResponse.STATUS_DOES_NOT_EXIST: - (PayloadDoesNotExistError, PayloadDoesNotExistError.__doc__), + (PayloadDoesNotExistError, PayloadDoesNotExistError.__doc__), payload_registration_protos.UpdatePayloadVersionResponse.STATUS_INVALID_CREDENTIALS: - (InvalidPayloadCredentialsError, InvalidPayloadCredentialsError.__doc__), + (InvalidPayloadCredentialsError, InvalidPayloadCredentialsError.__doc__), }) @@ -767,9 +910,9 @@

    Source code for bosdyn.client.payload_registration

    _GET_PAYLOAD_AUTH_TOKEN_STATUS_TO_ERROR.update({ payload_registration_protos.GetPayloadAuthTokenResponse.STATUS_OK: (None, None), payload_registration_protos.GetPayloadAuthTokenResponse.STATUS_INVALID_CREDENTIALS: - (InvalidPayloadCredentialsError, InvalidPayloadCredentialsError.__doc__), + (InvalidPayloadCredentialsError, InvalidPayloadCredentialsError.__doc__), payload_registration_protos.GetPayloadAuthTokenResponse.STATUS_PAYLOAD_NOT_AUTHORIZED: - (PayloadNotAuthorizedError, PayloadNotAuthorizedError.__doc__), + (PayloadNotAuthorizedError, PayloadNotAuthorizedError.__doc__), }) @@ -783,6 +926,29 @@

    Source code for bosdyn.client.payload_registration

    status_to_string=payload_registration_protos.GetPayloadAuthTokenResponse.Status.Name, status_to_error=_GET_PAYLOAD_AUTH_TOKEN_STATUS_TO_ERROR) +# Associate proto status errors to python client errors for UpdatePayloadAttachedResponse +_UPDATE_PAYLOAD_ATTACHED_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None)) +_UPDATE_PAYLOAD_ATTACHED_STATUS_TO_ERROR.update({ + payload_registration_protos.UpdatePayloadAttachedResponse.STATUS_OK: (None, None), + payload_registration_protos.UpdatePayloadAttachedResponse.STATUS_DOES_NOT_EXIST: + (PayloadDoesNotExistError, PayloadDoesNotExistError.__doc__), + payload_registration_protos.UpdatePayloadAttachedResponse.STATUS_INVALID_CREDENTIALS: + (InvalidPayloadCredentialsError, InvalidPayloadCredentialsError.__doc__), + payload_registration_protos.UpdatePayloadAttachedResponse.STATUS_PAYLOAD_NOT_AUTHORIZED: + (PayloadNotAuthorizedError, PayloadNotAuthorizedError.__doc__), +}) + + +# Function to parse all types of errors from update payload attached request +@handle_common_header_errors +@handle_unset_status_error(unset='STATUS_UNKNOWN') +def _update_payload_attached_error(response): + """Return a custom exception based on response, None if no error.""" + return error_factory( + response, response.status, + status_to_string=payload_registration_protos.UpdatePayloadAttachedResponse.Status.Name, + status_to_error=_UPDATE_PAYLOAD_ATTACHED_STATUS_TO_ERROR) +
    [docs]class PayloadRegistrationKeepAlive(object): """Helper class to keep a payload entry registered. @@ -838,7 +1004,8 @@

    Source code for bosdyn.client.payload_registration

    self.pay_reg_client.register_payload(self.payload, self.secret) except PayloadAlreadyExistsError as exc: # If the payload exists, log a warning and continue. - self.logger.warning('Got a "payload already exists" error: %s\nContinuing to start thread.', str(exc)) + self.logger.warning( + 'Got a "payload already exists" error: %s\nContinuing to start thread.', str(exc)) else: self.logger.info('Payload registered.') diff --git a/docs/html/_modules/bosdyn/client/point_cloud.html b/docs/html/_modules/bosdyn/client/point_cloud.html index bb76176f6..cce23895d 100644 --- a/docs/html/_modules/bosdyn/client/point_cloud.html +++ b/docs/html/_modules/bosdyn/client/point_cloud.html @@ -7,7 +7,7 @@ - bosdyn.client.point_cloud — Spot 2.3.5 documentation + bosdyn.client.point_cloud — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -564,11 +605,13 @@

    Source code for bosdyn.client.point_cloud

     import bosdyn.api.point_cloud_service_pb2_grpc as point_cloud_service
     
     from .common import BaseClient
    -from bosdyn.client.common import (error_factory, error_pair, common_header_errors, handle_common_header_errors)
    +from bosdyn.client.common import (error_factory, error_pair, common_header_errors,
    +                                  handle_common_header_errors)
     from bosdyn.client.exceptions import ResponseError, UnsetStatusError
     
     LOGGER = logging.getLogger('point_cloud_client')
     
    +
     
    [docs]class PointCloudResponseError(ResponseError): """General class of errors for PointCloud service."""
    @@ -588,12 +631,17 @@

    Source code for bosdyn.client.point_cloud

     _STATUS_TO_ERROR = collections.defaultdict(lambda: (PointCloudResponseError, None))
     _STATUS_TO_ERROR.update({
         point_cloud_protos.PointCloudResponse.STATUS_OK: (None, None),
    -    point_cloud_protos.PointCloudResponse.STATUS_UNKNOWN_SOURCE: error_pair(UnknownPointCloudSourceError),
    -    point_cloud_protos.PointCloudResponse.STATUS_SOURCE_DATA_ERROR: error_pair(SourceDataError),
    -    point_cloud_protos.PointCloudResponse.STATUS_UNKNOWN: error_pair(UnsetStatusError),
    -    point_cloud_protos.PointCloudResponse.STATUS_POINT_CLOUD_DATA_ERROR: error_pair(PointCloudDataError),
    +    point_cloud_protos.PointCloudResponse.STATUS_UNKNOWN_SOURCE:
    +        error_pair(UnknownPointCloudSourceError),
    +    point_cloud_protos.PointCloudResponse.STATUS_SOURCE_DATA_ERROR:
    +        error_pair(SourceDataError),
    +    point_cloud_protos.PointCloudResponse.STATUS_UNKNOWN:
    +        error_pair(UnsetStatusError),
    +    point_cloud_protos.PointCloudResponse.STATUS_POINT_CLOUD_DATA_ERROR:
    +        error_pair(PointCloudDataError),
     })
     
    +
     @handle_common_header_errors
     def _error_from_response(response):
         """Return a custom exception based on the first invalid point_cloud response, None if no error."""
    @@ -606,6 +654,7 @@ 

    Source code for bosdyn.client.point_cloud

                 return result
         return None
     
    +
     
    [docs]class PointCloudClient(BaseClient): """A client handling point clouds.""" @@ -631,8 +680,8 @@

    Source code for bosdyn.client.point_cloud

     
    [docs] def list_point_cloud_sources_async(self, **kwargs): """Async version of list_point_cloud_sources()""" req = self._get_list_point_cloud_source_request() - return self.call_async(self._stub.ListPointCloudSources, req, _list_point_cloud_sources_value, - common_header_errors, **kwargs)
    + return self.call_async(self._stub.ListPointCloudSources, req, + _list_point_cloud_sources_value, common_header_errors, **kwargs)
    [docs] def get_point_cloud_from_sources(self, point_cloud_sources, **kwargs): """Obtain point clouds from sources using default parameters. @@ -651,12 +700,13 @@

    Source code for bosdyn.client.point_cloud

                 UnsetStatusError: An internal PointCloudService issue has happened
                 PointCloudDataError: Problem with the point cloud data. Only PointCloudSource is filled
             """
    -        return self.get_point_cloud([build_pc_request(src) for src in point_cloud_sources], **kwargs)
    + return self.get_point_cloud([build_pc_request(src) for src in point_cloud_sources], + **kwargs)
    [docs] def get_point_cloud_from_sources_async(self, point_cloud_sources, **kwargs): """Obtain point clouds from sources using default parameters.""" return self.get_point_cloud_async([build_pc_request(src) for src in point_cloud_sources], - **kwargs)
    + **kwargs)
    [docs] def get_point_cloud(self, point_cloud_requests, **kw_args): """Get the most recent point cloud @@ -714,6 +764,7 @@

    Source code for bosdyn.client.point_cloud

         def _get_list_point_cloud_source_request():
             return point_cloud_protos.ListPointCloudSourcesRequest()
    +
    [docs]def build_pc_request(point_cloud_source_name): """Helper function which builds an PointCloudRequest from an point cloud source name. @@ -725,6 +776,7 @@

    Source code for bosdyn.client.point_cloud

         """
         return point_cloud_protos.PointCloudRequest(point_cloud_source_name=point_cloud_source_name)
    + def _list_point_cloud_sources_value(response): return response.point_cloud_sources diff --git a/docs/html/_modules/bosdyn/client/power.html b/docs/html/_modules/bosdyn/client/power.html index 7574ffb8f..9c15b694d 100644 --- a/docs/html/_modules/bosdyn/client/power.html +++ b/docs/html/_modules/bosdyn/client/power.html @@ -7,7 +7,7 @@ - bosdyn.client.power — Spot 2.3.5 documentation + bosdyn.client.power — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -591,15 +632,15 @@

    Source code for bosdyn.client.power

     
     
     
    [docs]class EstoppedError(PowerResponseError): - """Cannot power on while estopped. + """Cannot power on while estopped; inspect EStopState for more info."""
    - Inspect EStopState for more info."""
    +
    [docs]class OverriddenError(PowerResponseError): + """The command was overridden and is no longer valid."""
    -
    [docs]class FaultedError(PowerResponseError): - """Cannot power on due to a fault. - Inspect FaultState for more info."""
    +
    [docs]class FaultedError(PowerResponseError): + """Cannot power on due to a fault; inspect FaultState for more info."""
    [docs]class PowerError(Error): @@ -716,6 +757,7 @@

    Source code for bosdyn.client.power

         power_pb2.STATUS_FAULTED: (FaultedError, FaultedError.__doc__),
         power_pb2.STATUS_INTERNAL_ERROR: (InternalServerError, InternalServerError.__doc__),
         power_pb2.STATUS_LICENSE_ERROR: (LicenseError, LicenseError.__doc__),
    +    power_pb2.STATUS_OVERRIDDEN: (OverriddenError, OverriddenError.__doc__),
     })
     
     
    @@ -723,7 +765,14 @@ 

    Source code for bosdyn.client.power

         return response.status
     
     
    -
    [docs]def safe_power_off(command_client, state_client, timeout_sec=30, update_frequency=1.0, **kwargs): +
    [docs]@deprecated(reason='Replaced by the less ambiguous safe_power_off_motors function.', version='3.0.0', + action="ignore") +def safe_power_off(command_client, state_client, timeout_sec=30, update_frequency=1.0, **kwargs): + """Safely power off motors. See safe_power_off_motors().""" + safe_power_off_motors(command_client, state_client, timeout_sec, update_frequency, **kwargs)
    + + +
    [docs]def safe_power_off_motors(command_client, state_client, timeout_sec=30, update_frequency=1.0, **kwargs): """Power off robot motors safely. This function blocks until robot safely powers off. This means the robot will attempt to sit before powering motors off. @@ -813,9 +862,34 @@

    Source code for bosdyn.client.power

         _power_command(power_client, request, timeout_sec, update_frequency, **kwargs)
    +
    [docs]def safe_power_off_robot(command_client, state_client, power_client, timeout_sec=30, + update_frequency=1.0, **kwargs): + """Power off the robot motors and then the robot computers safely. This function blocks until + robot safely powers off. This means the robot will attempt to sit before powering motors off. + + Args: + command_client (RobotCommandClient): client for calling RobotCommandService safe power off. + state_client (RobotStateClient): client for monitoring power state. + power_client (bosdyn.api.PowerClient): client for calling power service. + timeout_sec (float): Max time this function will block for. + update_frequency (float): The frequency with which the robot should check if the command + has succeeded. + + Raises: + RpcError: Problem communicating with the robot. + power.CommandTimedOutError: Did not power off within timeout_sec + RobotCommandResponseError: Something went wrong with the safe power off. + """ + end_time = time.time() + timeout_sec + safe_power_off_motors(command_client, state_client, timeout_sec=end_time - time.time(), + update_frequency=update_frequency, **kwargs) + power_off_robot(power_client, timeout_sec=end_time - time.time(), + update_frequency=update_frequency, **kwargs)
    + +
    [docs]def power_off_robot(power_client, timeout_sec=30, update_frequency=1.0, **kwargs): """Fully power off the robot. Powering off the robot will stop API comms. - + Args: power_client (bosdyn.api.PowerClient): client for calling power service. timeout_sec (float): Max time this function will block for. @@ -831,9 +905,34 @@

    Source code for bosdyn.client.power

                        **kwargs)
    +
    [docs]def safe_power_cycle_robot(command_client, state_client, power_client, timeout_sec=30, + update_frequency=1.0, **kwargs): + """Power cycle the robot safely. This function blocks until robot safely powers off. The robot + will attempt to sit before powering cycling. + + Args: + command_client (RobotCommandClient): client for calling RobotCommandService safe power off. + state_client (RobotStateClient): client for monitoring power state. + power_client (bosdyn.api.PowerClient): client for calling power service. + timeout_sec (float): Max time this function will block for. + update_frequency (float): The frequency with which the robot should check if the command + has succeeded. + + Raises: + RpcError: Problem communicating with the robot. + power.CommandTimedOutError: Did not power off within timeout_sec + RobotCommandResponseError: Something went wrong with the safe power off. + """ + end_time = time.time() + timeout_sec + safe_power_off_motors(command_client, state_client, timeout_sec=end_time - time.time(), + update_frequency=update_frequency, **kwargs) + power_cycle_robot(power_client, timeout_sec=end_time - time.time(), + update_frequency=update_frequency, **kwargs)
    + +
    [docs]def power_cycle_robot(power_client, timeout_sec=30, update_frequency=1.0, **kwargs): """Power cycle the robot. Power cycling the robot will stop API comms. - + Args: power_client (bosdyn.api.PowerClient): client for calling power service. timeout_sec (float): Max time this function will block for. @@ -851,7 +950,7 @@

    Source code for bosdyn.client.power

     
     
    [docs]def power_off_payload_ports(power_client, timeout_sec=30, update_frequency=1.0, **kwargs): """Power off the robot payload ports. - + Args: power_client (bosdyn.api.PowerClient): client for calling power service. timeout_sec (float): Max time this function will block for. @@ -868,7 +967,7 @@

    Source code for bosdyn.client.power

     
     
    [docs]def power_on_payload_ports(power_client, timeout_sec=30, update_frequency=1.0, **kwargs): """Power on the robot payload ports. - + Args: power_client (bosdyn.api.PowerClient): client for calling power service. timeout_sec (float): Max time this function will block for. @@ -885,7 +984,7 @@

    Source code for bosdyn.client.power

     
     
    [docs]def power_off_wifi_radio(power_client, timeout_sec=30, update_frequency=1.0, **kwargs): """Power off the robot Wi-Fi radio. - + Args: power_client (bosdyn.api.PowerClient): client for calling power service. timeout_sec (float): Max time this function will block for. @@ -902,7 +1001,7 @@

    Source code for bosdyn.client.power

     
     
    [docs]def power_on_wifi_radio(power_client, timeout_sec=30, update_frequency=1.0, **kwargs): """Power off the robot Wi-Fi radio. - + Args: power_client (bosdyn.api.PowerClient): client for calling power service. timeout_sec (float): Max time this function will block for. @@ -924,7 +1023,7 @@

    Source code for bosdyn.client.power

     def _power_command(power_client, request, timeout_sec=30, update_frequency=1.0,
                        expect_grpc_timeout=False, **kwargs):
         """Helper function to issue command to power client.
    -    
    +
         Args:
             power_client (bosdyn.api.PowerClient): Client for calling power service.
             request (bosdyn.api.PowerCommandRequest): Request to make to power service.
    diff --git a/docs/html/_modules/bosdyn/client/processors.html b/docs/html/_modules/bosdyn/client/processors.html
    index 85d4bfa0f..3e87ac280 100644
    --- a/docs/html/_modules/bosdyn/client/processors.html
    +++ b/docs/html/_modules/bosdyn/client/processors.html
    @@ -7,7 +7,7 @@
       
       
       
    -  bosdyn.client.processors — Spot 2.3.5 documentation
    +  bosdyn.client.processors — Spot 3.0.0 documentation
       
     
       
    @@ -66,7 +66,7 @@
                 
                 
                   
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/client/recording.html b/docs/html/_modules/bosdyn/client/recording.html index c32bb9b74..d4570fac9 100644 --- a/docs/html/_modules/bosdyn/client/recording.html +++ b/docs/html/_modules/bosdyn/client/recording.html @@ -7,7 +7,7 @@ - bosdyn.client.recording — Spot 2.3.5 documentation + bosdyn.client.recording — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -871,6 +912,10 @@

    Source code for bosdyn.client.recording

         """The robot is not localized to the existing map and cannot start recording."""
    +
    [docs]class TooFarFromExistingMapError(RecordingServiceResponseError): + """The robot is too far from the existing map and cannot start recording."""
    + +
    [docs]class RemoteCloudFailureNotInDirectoryError(RecordingServiceResponseError): """Failed to start recording because a remote point cloud (e.g. a LIDAR) is not registered to the service directory."""
    @@ -922,7 +967,9 @@

    Source code for bosdyn.client.recording

         recording_pb2.StartRecordingResponse.STATUS_REMOTE_CLOUD_FAILURE_NO_DATA:
             (RemoteCloudFailureNoDataError, RemoteCloudFailureNoDataError.__doc__),
         recording_pb2.StartRecordingResponse.STATUS_FIDUCIAL_POSE_NOT_OK:
    -        (FiducialPoseError, FiducialPoseError.__doc__)
    +        (FiducialPoseError, FiducialPoseError.__doc__),
    +    recording_pb2.StartRecordingResponse.STATUS_TOO_FAR_FROM_EXISTING_MAP:
    +        (TooFarFromExistingMapError, TooFarFromExistingMapError.__doc__)
     })
     
     
    diff --git a/docs/html/_modules/bosdyn/client/robot.html b/docs/html/_modules/bosdyn/client/robot.html
    index 20501f01a..e553c306e 100644
    --- a/docs/html/_modules/bosdyn/client/robot.html
    +++ b/docs/html/_modules/bosdyn/client/robot.html
    @@ -7,7 +7,7 @@
       
       
       
    -  bosdyn.client.robot — Spot 2.3.5 documentation
    +  bosdyn.client.robot — Spot 3.0.0 documentation
       
     
       
    @@ -66,7 +66,7 @@
                 
                 
                   
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -556,20 +597,22 @@

    Source code for bosdyn.client.robot

     import logging
     import time
     
    +import bosdyn.api.data_buffer_pb2 as data_buffer_protos
     import bosdyn.client.channel
     from bosdyn.util import timestamp_to_sec
     
     from .auth import AuthClient
     from .channel import DEFAULT_MAX_MESSAGE_LENGTH
     from .data_buffer import DataBufferClient
    -from .data_service import DataServiceClient
    +from .data_buffer import log_event as pkg_log_event
     from .directory import DirectoryClient
     from .directory_registration import DirectoryRegistrationClient
     from .estop import EstopClient
     from .estop import is_estopped as pkg_is_estopped
     from .exceptions import Error
     from .lease import LeaseWallet
    -from .payload_registration import PayloadRegistrationClient, PayloadAlreadyExistsError, PayloadNotAuthorizedError
    +from .payload_registration import (PayloadRegistrationClient, PayloadAlreadyExistsError,
    +                                   PayloadNotAuthorizedError)
     from .power import PowerClient
     from .power import power_on as pkg_power_on
     from .power import power_off as pkg_power_off
    @@ -635,8 +678,6 @@ 

    Source code for bosdyn.client.robot

     
         _bootstrap_service_authorities = {
             AuthClient.default_service_name: 'auth.spot.robot',
    -        DataBufferClient.default_service_name: 'buffer.spot.robot',
    -        DataServiceClient.default_service_name: 'data.spot.robot',
             DirectoryClient.default_service_name: 'api.spot.robot',
             DirectoryRegistrationClient.default_service_name: 'api.spot.robot',
             PayloadRegistrationClient.default_service_name: 'payload-registration.spot.robot',
    @@ -679,18 +720,24 @@ 

    Source code for bosdyn.client.robot

             self.max_send_message_length = DEFAULT_MAX_MESSAGE_LENGTH
             self.max_receive_message_length = DEFAULT_MAX_MESSAGE_LENGTH
     
    -    def __del__(self):
    +    def _shutdown(self):
    +        """Shut down background threads for tokens and time sync."""
             if self._time_sync_thread:
                 self._time_sync_thread.stop()
    +            self._time_sync_thread = None
             if self._token_manager:
                 self._token_manager.stop()
    +            self._token_manager = None
     
         def __exit__(self, exc_type, exc_val, exc_tb):
    -        self.__del__()
    +        self._shutdown()
     
         def __enter__(self):
             return self
     
    +    def __del__(self):
    +        self._shutdown()
    +
         def _get_token_id(self, username):
             return '{}.{}'.format(self.serial_number, username)
     
    @@ -812,13 +859,6 @@ 

    Source code for bosdyn.client.robot

                 UnregisteredServiceNameError: service_name is unknown.
             """
     
    -        # Update max send/receive message lengths.
    -        if 'grpc.max_receive_message_length' not in [option[0] for option in options]:
    -            options.append(('grpc.max_receive_message_length', self.max_receive_message_length))
    -        if 'grpc.max_send_message_length' not in [option[0] for option in options]:
    -            options.append(('grpc.max_send_message_length', self.max_send_message_length))
    -
    -
             # If a specific channel was not set, look up the authority so we can get a channel.
             # Get the authority from either
             #   1. The bootstrap authority for this client_class, if available
    @@ -844,6 +884,12 @@ 

    Source code for bosdyn.client.robot

             if authority in self.channels_by_authority:
                 return self.channels_by_authority[authority]
     
    +        # Update max send/receive message lengths.
    +        if 'grpc.max_receive_message_length' not in [option[0] for option in options]:
    +            options.append(('grpc.max_receive_message_length', self.max_receive_message_length))
    +        if 'grpc.max_send_message_length' not in [option[0] for option in options]:
    +            options.append(('grpc.max_send_message_length', self.max_send_message_length))
    +
             if skip_app_token_check:
                 should_send_app_token = False
             else:
    @@ -943,7 +989,7 @@ 

    Source code for bosdyn.client.robot

                 except PayloadNotAuthorizedError:
                     if not printed_warning:
                         printed_warning = True
    -                    print('Payload is not authorized. Authentication will block until an'
    +                    self.logger.warn('Payload is not authorized. Authentication will block until an'
                               ' operator authorizes the payload in the Admin Console.')
                     pass
                 time.sleep(0.1)
    @@ -1081,6 +1127,26 @@ 

    Source code for bosdyn.client.robot

                 robot_timestamp = self.time_sync.robot_timestamp_from_local_secs(timestamp_secs)
             client.add_operator_comment(comment, robot_timestamp=robot_timestamp, timeout=timeout)
    +
    [docs] def log_event( # pylint: disable=too-many-arguments,no-member + self, event_type, level, description, start_timestamp_secs, end_timestamp_secs=None, + id_str=None, parameters=None, + log_preserve_hint=data_buffer_protos.Event.LOG_PRESERVE_HINT_NORMAL): + """Add an Event to the Data Buffer. + + Args: + event_type (string): The type of event. + level (bosdyn.api.Event.Level): The relative importance of the event. + description (string): A human-readable description of the event. + start_timestamp_secs (float): Start of the event, in local time. + end_timestamp_secs (float): End of the event. start_timestamp_secs is used if None. + id_str (string): Unique id for event. A uuid is generated if None. + parameters ([bosdyn.api.Parameter]): Parameters to attach to the event. + """ + return pkg_log_event(self, event_type=event_type, level=level, description=description, + start_timestamp_secs=start_timestamp_secs, + end_timestamp_secs=end_timestamp_secs, id_str=id_str, + parameters=parameters, log_preserve_hint=log_preserve_hint)
    +
    [docs] def power_on(self, timeout_sec=20, update_frequency=1.0, timeout=None): """Power on robot. This function blocks until robot powers on. @@ -1195,6 +1261,7 @@

    Source code for bosdyn.client.robot

             self._has_arm = pkg_has_arm(state_client, timeout=timeout)
             return self._has_arm
    +
    diff --git a/docs/html/_modules/bosdyn/client/robot_command.html b/docs/html/_modules/bosdyn/client/robot_command.html index cf22ecc34..411f62eb9 100644 --- a/docs/html/_modules/bosdyn/client/robot_command.html +++ b/docs/html/_modules/bosdyn/client/robot_command.html @@ -7,7 +7,7 @@ - bosdyn.client.robot_command — Spot 2.3.5 documentation + bosdyn.client.robot_command — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -556,7 +597,7 @@

    Source code for bosdyn.client.robot_command

     import time
     
     from deprecated.sphinx import deprecated
    -from google.protobuf import any_pb2
    +from google.protobuf import any_pb2, wrappers_pb2
     from bosdyn import geometry
     
     from bosdyn.api import geometry_pb2
    @@ -579,7 +620,7 @@ 

    Source code for bosdyn.client.robot_command

     from .exceptions import ResponseError, InvalidRequestError, TimedOutError, UnsetStatusError
     from .exceptions import Error as BaseError
     from .frame_helpers import BODY_FRAME_NAME, ODOM_FRAME_NAME, get_se2_a_tform_b
    -from .math_helpers import SE2Pose
    +from .math_helpers import SE2Pose, SE3Pose
     from .lease import add_lease_wallet_processors
     
     # The angles (in radians) that represent the claw gripper open and closed positions.
    @@ -610,9 +651,11 @@ 

    Source code for bosdyn.client.robot_command

     
    [docs]class NotPoweredOnError(RobotCommandResponseError): """The robot must be powered on to accept a command."""
    +
    [docs]class BehaviorFaultError(RobotCommandResponseError): """The robot may not be commanded with uncleared behavior faults."""
    +
    [docs]class NotClearedError(RobotCommandResponseError): """Behavior fault could not be cleared."""
    @@ -628,6 +671,7 @@

    Source code for bosdyn.client.robot_command

     
    [docs]class CommandTimedOutError(Error): """Timed out waiting for SUCCESS response from robot command."""
    +
    [docs]class UnknownFrameError(RobotCommandResponseError): """Robot does not know how to handle supplied frame."""
    @@ -724,7 +768,7 @@

    Source code for bosdyn.client.robot_command

                     }
                 }
             },
    -        'gripper_command':{
    +        'gripper_command': {
                 '@command': {
                     'claw_gripper_command': {
                         'trajectory': {
    @@ -785,7 +829,8 @@ 

    Source code for bosdyn.client.robot_command

                 # Recursion into a one-of message. '@key' means field 'key' contains a one-of message.
                 which_oneof = proto.WhichOneof(key[1:])
                 if not which_oneof or which_oneof not in subtree:
    -                return  # No submessage, or tree doesn't contain a conversion for it.
    +                # No submessage, or tree doesn't contain a conversion for it.
    +                return
                 _edit_proto(getattr(proto, which_oneof), subtree[which_oneof], edit_fn)
             elif subtree:
                 # Recursion into a sub-message by field name.
    @@ -999,7 +1044,8 @@ 

    Source code for bosdyn.client.robot_command

             def _to_robot_time(key, proto):
                 """If proto has a field named key with a timestamp, convert timestamp to robot time."""
                 if not (key in proto.DESCRIPTOR.fields_by_name and proto.HasField(key)):
    -                return  # No such field in proto, or field does not contain a timestamp.
    +                # No such field in proto, or field does not contain a timestamp.
    +                return
                 timestamp = getattr(proto, key)
                 converter.convert_timestamp_from_local_to_robot(timestamp)
     
    @@ -1050,7 +1096,8 @@ 

    Source code for bosdyn.client.robot_command

     
     
     # yapf: disable
    -_ROBOT_COMMAND_STATUS_TO_ERROR = collections.defaultdict(lambda: (RobotCommandResponseError, None))
    +_ROBOT_COMMAND_STATUS_TO_ERROR = collections.defaultdict(
    +    lambda: (RobotCommandResponseError, None))
     _ROBOT_COMMAND_STATUS_TO_ERROR.update({
         robot_command_pb2.RobotCommandResponse.STATUS_OK: (None, None),
         robot_command_pb2.RobotCommandResponse.STATUS_INVALID_REQUEST: error_pair(InvalidRequestError),
    @@ -1111,7 +1158,8 @@ 

    Source code for bosdyn.client.robot_command

     
     
     # yapf: disable
    -_CLEAR_BEHAVIOR_FAULT_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
    +_CLEAR_BEHAVIOR_FAULT_STATUS_TO_ERROR = collections.defaultdict(
    +    lambda: (ResponseError, None))
     _CLEAR_BEHAVIOR_FAULT_STATUS_TO_ERROR.update({
         robot_command_pb2.ClearBehaviorFaultResponse.STATUS_CLEARED: (None, None),
         robot_command_pb2.ClearBehaviorFaultResponse.STATUS_NOT_CLEARED:
    @@ -1219,6 +1267,31 @@ 

    Source code for bosdyn.client.robot_command

                 safe_power_off_request=basic_command_pb2.SafePowerOffCommand.Request())
             command = robot_command_pb2.RobotCommand(full_body_command=full_body_command)
             return command
    + +
    [docs] @staticmethod + def constrained_manipulation_command(task_type, init_wrench_direction_in_frame_name, + force_limit, torque_limit, frame_name, + tangential_speed=None, rotational_speed=None): + """Command constrained manipulation. """ + if (tangential_speed is not None): + full_body_command = full_body_command_pb2.FullBodyCommand.Request( + constrained_manipulation_request=basic_command_pb2.ConstrainedManipulationCommand. + Request(task_type=task_type, + init_wrench_direction_in_frame_name=init_wrench_direction_in_frame_name, + frame_name=frame_name, tangential_speed=tangential_speed)) + elif (rotational_speed is not None): + full_body_command = full_body_command_pb2.FullBodyCommand.Request( + constrained_manipulation_request=basic_command_pb2.ConstrainedManipulationCommand. + Request(task_type=task_type, + init_wrench_direction_in_frame_name=init_wrench_direction_in_frame_name, + frame_name=frame_name, rotational_speed=rotational_speed)) + else: + raise Exception("Need either translational or rotational speed") + full_body_command.constrained_manipulation_request.force_limit.value = force_limit + full_body_command.constrained_manipulation_request.torque_limit.value = torque_limit + command = robot_command_pb2.RobotCommand(full_body_command=full_body_command) + return command
    + ################################### # Mobility commands - DEPRECATED # ################################### @@ -1263,11 +1336,8 @@

    Source code for bosdyn.client.robot_command

             return command
    [docs] @staticmethod - @deprecated( - reason='Mobility commands are now sent as a part of synchronized commands. '\ - 'Use synchro_velocity_command instead.', - version='2.1.0', - action="always") + @deprecated(reason='Mobility commands are now sent as a part of synchronized commands. ' + 'Use synchro_velocity_command instead.', version='2.1.0', action="always") def velocity_command(v_x, v_y, v_rot, params=None, body_height=0.0, locomotion_hint=spot_command_pb2.HINT_AUTO, frame_name=BODY_FRAME_NAME): """ @@ -1304,11 +1374,8 @@

    Source code for bosdyn.client.robot_command

             return command
    [docs] @staticmethod - @deprecated( - reason='Mobility commands are now sent as a part of synchronized commands. '\ - 'Use synchro_stand_command instead.', - version='2.1.0', - action="always") + @deprecated(reason='Mobility commands are now sent as a part of synchronized commands. ' + 'Use synchro_stand_command instead.', version='2.1.0', action="always") def stand_command(params=None, body_height=0.0, footprint_R_body=geometry.EulerZXY()): """ Command robot to stand. If the robot is sitting, it will stand up. If the robot is @@ -1337,11 +1404,8 @@

    Source code for bosdyn.client.robot_command

             return command
    [docs] @staticmethod - @deprecated( - reason='Mobility commands are now sent as a part of synchronized commands. '\ - 'Use synchro_sit_command instead.', - version='2.1.0', - action="always") + @deprecated(reason='Mobility commands are now sent as a part of synchronized commands. ' + 'Use synchro_sit_command instead.', version='2.1.0', action="always") def sit_command(params=None): """ Command the robot to sit. @@ -1360,7 +1424,6 @@

    Source code for bosdyn.client.robot_command

             command = robot_command_pb2.RobotCommand(mobility_command=mobility_command)
             return command
    - ######################### # Synchronized commands # ######################### @@ -1443,8 +1506,9 @@

    Source code for bosdyn.client.robot_command

             return robot_command
    [docs] @staticmethod - def synchro_trajectory_command_in_body_frame(goal_x_rt_body, goal_y_rt_body, goal_heading_rt_body, - frame_tree_snapshot, params=None, body_height=0.0, + def synchro_trajectory_command_in_body_frame(goal_x_rt_body, goal_y_rt_body, + goal_heading_rt_body, frame_tree_snapshot, + params=None, body_height=0.0, locomotion_hint=spot_command_pb2.HINT_AUTO): """Command robot to move to pose described relative to the robots body along a 2D plane. For example, a command to move forward 2 meters at the same heading will have goal_x_rt_body=2.0, goal_y_rt_body=0.0, @@ -1476,8 +1540,8 @@

    Source code for bosdyn.client.robot_command

             odom_tform_body = get_se2_a_tform_b(frame_tree_snapshot, ODOM_FRAME_NAME, BODY_FRAME_NAME)
             odom_tform_goto = odom_tform_body * goto_rt_body
             return RobotCommandBuilder.synchro_se2_trajectory_command(odom_tform_goto.to_proto(),
    -                                                                 ODOM_FRAME_NAME,
    -                                                                 params, body_height, locomotion_hint)
    + ODOM_FRAME_NAME, params, + body_height, locomotion_hint)
    [docs] @staticmethod def synchro_velocity_command(v_x, v_y, v_rot, params=None, body_height=0.0, @@ -1685,7 +1749,9 @@

    Source code for bosdyn.client.robot_command

             return robot_command
     
     
    [docs] @staticmethod - def arm_gaze_command(x, y, z, frame_name, build_on_command=None): + def arm_gaze_command(x, y, z, frame_name, build_on_command=None, frame2_tform_desired_hand=None, + frame2_name=None, max_linear_vel=None, max_angular_vel=None, + max_accel=None): """ Builds a Vec3Trajectory to tell the robot arm to gaze at a point in 3D space. Returns: RobotCommand, which can be issued to the robot command service @@ -1694,10 +1760,26 @@

    Source code for bosdyn.client.robot_command

             point1 = trajectory_pb2.Vec3TrajectoryPoint(point=pos)
     
             traj = trajectory_pb2.Vec3Trajectory(points=[point1])
    -
             # Build the proto
             gaze_cmd = arm_command_pb2.GazeCommand.Request(target_trajectory_in_frame1=traj,
                                                            frame1_name=frame_name)
    +
    +        if frame2_tform_desired_hand is not None and frame2_name is not None:
    +            if type(frame2_tform_desired_hand) == SE3Pose:
    +                # Convert input argument from math_helpers class to protobuf message.
    +                frame2_tform_desired_hand = frame2_tform_desired_hand.to_proto()
    +
    +            desired_point = trajectory_pb2.SE3TrajectoryPoint(pose=frame2_tform_desired_hand)
    +            gaze_cmd.tool_trajectory_in_frame2.points.extend([desired_point])
    +            gaze_cmd.frame2_name = frame2_name
    +
    +        if max_linear_vel is not None:
    +            gaze_cmd.max_linear_velocity.value = max_linear_vel
    +        if max_angular_vel is not None:
    +            gaze_cmd.max_angular_velocity.value = max_angular_vel
    +        if max_accel is not None:
    +            gaze_cmd.maximum_acceleration.value = max_accel
    +
             arm_command = arm_command_pb2.ArmCommand.Request(arm_gaze_command=gaze_cmd)
             synchronized_command = synchronized_command_pb2.SynchronizedCommand.Request(
                 arm_command=arm_command)
    @@ -1734,7 +1816,8 @@ 

    Source code for bosdyn.client.robot_command

             return robot_command
    [docs] @staticmethod - def arm_wrench_command(force_x, force_y, force_z, torque_x, torque_y, torque_z, frame_name, seconds=5, build_on_command=None): + def arm_wrench_command(force_x, force_y, force_z, torque_x, torque_y, torque_z, frame_name, + seconds=5, build_on_command=None): """ Builds a command to tell robot arm to exhibit a wrench. Wraps it in a SynchronizedCommand. @@ -1749,7 +1832,6 @@

    Source code for bosdyn.client.robot_command

                                                               time_since_reference=duration)
             trajectory = trajectory_pb2.WrenchTrajectory(points=[traj_point])
     
    -
             arm_cartesian_command = arm_command_pb2.ArmCartesianCommand.Request(
                 root_frame_name=frame_name, wrench_trajectory_in_task=trajectory,
                 x_axis=arm_command_pb2.ArmCartesianCommand.Request.AXIS_MODE_FORCE,
    @@ -1757,8 +1839,7 @@ 

    Source code for bosdyn.client.robot_command

                 z_axis=arm_command_pb2.ArmCartesianCommand.Request.AXIS_MODE_FORCE,
                 rx_axis=arm_command_pb2.ArmCartesianCommand.Request.AXIS_MODE_FORCE,
                 ry_axis=arm_command_pb2.ArmCartesianCommand.Request.AXIS_MODE_FORCE,
    -            rz_axis=arm_command_pb2.ArmCartesianCommand.Request.AXIS_MODE_FORCE
    -            )
    +            rz_axis=arm_command_pb2.ArmCartesianCommand.Request.AXIS_MODE_FORCE)
             arm_command = arm_command_pb2.ArmCommand.Request(
                 arm_cartesian_command=arm_cartesian_command)
             synchronized_command = synchronized_command_pb2.SynchronizedCommand.Request(
    @@ -1813,10 +1894,44 @@ 

    Source code for bosdyn.client.robot_command

                 return RobotCommandBuilder.build_synchro_command(build_on_command, command)
             return command
    +
    [docs] @staticmethod + def create_arm_joint_trajectory_point(sh0, sh1, el0, el1, wr0, wr1, + time_since_reference_secs=None): + joint_position = arm_command_pb2.ArmJointPosition( + sh0=wrappers_pb2.DoubleValue(value=sh0), sh1=wrappers_pb2.DoubleValue(value=sh1), + el0=wrappers_pb2.DoubleValue(value=el0), el1=wrappers_pb2.DoubleValue(value=el1), + wr0=wrappers_pb2.DoubleValue(value=wr0), wr1=wrappers_pb2.DoubleValue(value=wr1)) + if time_since_reference_secs is not None: + return arm_command_pb2.ArmJointTrajectoryPoint( + position=joint_position, + time_since_reference=seconds_to_duration(time_since_reference_secs)) + else: + return arm_command_pb2.ArmJointTrajectoryPoint(position=joint_position)
    + +
    [docs] @staticmethod + def arm_joint_command(sh0, sh1, el0, el1, wr0, wr1, max_vel=None, max_accel=None, + build_on_command=None): + traj_point1 = RobotCommandBuilder.create_arm_joint_trajectory_point( + sh0, sh1, el0, el1, wr0, wr1) + arm_joint_traj = arm_command_pb2.ArmJointTrajectory(points=[traj_point1]) + + if max_vel is not None: + arm_joint_traj.maximum_velocity.value = max_vel + if max_accel is not None: + arm_joint_traj.maximum_acceleration.value = max_accel + + joint_move_command = arm_command_pb2.ArmJointMoveCommand.Request(trajectory=arm_joint_traj) + arm_command = arm_command_pb2.ArmCommand.Request(arm_joint_move_command=joint_move_command) + sync_arm = synchronized_command_pb2.SynchronizedCommand.Request(arm_command=arm_command) + arm_sync_robot_cmd = robot_command_pb2.RobotCommand(synchronized_command=sync_arm) + if build_on_command: + return RobotCommandBuilder.build_synchro_command(build_on_command, arm_sync_robot_cmd) + return arm_sync_robot_cmd
    ######################## # Spot mobility params # ######################## +
    [docs] @staticmethod def mobility_params(body_height=0.0, footprint_R_body=geometry.EulerZXY(), locomotion_hint=spot_command_pb2.HINT_AUTO, stair_hint=False, @@ -1851,7 +1966,8 @@

    Source code for bosdyn.client.robot_command

                                                    stair_hint=stair_hint,
                                                    external_force_params=external_force_params)
    -
    [docs] def build_body_external_forces( +
    [docs] @staticmethod + def build_body_external_forces( external_force_indicator=spot_command_pb2.BodyExternalForceParams.EXTERNAL_FORCE_NONE, override_external_force_vec=None): """Helper to create Mobility params. @@ -1874,7 +1990,7 @@

    Source code for bosdyn.client.robot_command

             """
             if external_force_indicator == spot_command_pb2.BodyExternalForceParams.EXTERNAL_FORCE_USE_OVERRIDE:
                 if override_external_force_vec is None:
    -                #Default the override forces to all zeros if none are specified
    +                # Default the override forces to all zeros if none are specified
                     override_external_force_vec = (0.0, 0.0, 0.0)
                 ext_forces = geometry_pb2.Vec3(x=override_external_force_vec[0],
                                                y=override_external_force_vec[1],
    @@ -1882,10 +1998,10 @@ 

    Source code for bosdyn.client.robot_command

                 return spot_command_pb2.BodyExternalForceParams(
                     external_force_indicator=external_force_indicator, frame_name=BODY_FRAME_NAME,
                     external_force_override=ext_forces)
    -        elif (external_force_indicator ==
    -              spot_command_pb2.BodyExternalForceParams.EXTERNAL_FORCE_NONE or
    -              external_force_indicator ==
    -              spot_command_pb2.BodyExternalForceParams.EXTERNAL_FORCE_USE_ESTIMATE):
    +        elif (external_force_indicator
    +              == spot_command_pb2.BodyExternalForceParams.EXTERNAL_FORCE_NONE or
    +              external_force_indicator
    +              == spot_command_pb2.BodyExternalForceParams.EXTERNAL_FORCE_USE_ESTIMATE):
                 return spot_command_pb2.BodyExternalForceParams(
                     external_force_indicator=external_force_indicator)
             else:
    @@ -1987,8 +2103,12 @@ 

    Source code for bosdyn.client.robot_command

         raise CommandTimedOutError(
             "Took longer than {:.1f} seconds to assure the robot stood.".format(now - start_time))
    +
    [docs]def block_until_arm_arrives(command_client, cmd_id, timeout_sec=None): - """Helper that blocks until the arm arrives at the end of its current trajectory. + """Helper that blocks until the arm achieves a finishing state for the specific arm command. + + This helper will block and check the feedback for ArmCartesianCommand, GazeCommand, + ArmJointMoveCommand, and NamedArmPositionsCommand. Args: command_client: robot command client, used to request feedback @@ -2007,11 +2127,26 @@

    Source code for bosdyn.client.robot_command

     
         while timeout_sec is None or now < end_time:
             feedback_resp = command_client.robot_command_feedback(cmd_id)
    -
    -        if feedback_resp.feedback.synchronized_feedback.arm_command_feedback.arm_cartesian_feedback.status == arm_command_pb2.ArmCartesianCommand.Feedback.STATUS_TRAJECTORY_COMPLETE:
    -            return True
    -        elif feedback_resp.feedback.synchronized_feedback.arm_command_feedback.arm_cartesian_feedback.status == arm_command_pb2.ArmCartesianCommand.Feedback.STATUS_TRAJECTORY_STALLED or feedback_resp.feedback.synchronized_feedback.arm_command_feedback.arm_cartesian_feedback.status == arm_command_pb2.ArmCartesianCommand.Feedback.STATUS_TRAJECTORY_CANCELLED:
    -            return False
    +        arm_feedback = feedback_resp.feedback.synchronized_feedback.arm_command_feedback
    +
    +        if arm_feedback.HasField("arm_cartesian_feedback"):
    +            if arm_feedback.arm_cartesian_feedback.status == arm_command_pb2.ArmCartesianCommand.Feedback.STATUS_TRAJECTORY_COMPLETE:
    +                return True
    +            elif arm_feedback.arm_cartesian_feedback.status == arm_command_pb2.ArmCartesianCommand.Feedback.STATUS_TRAJECTORY_STALLED or feedback_resp.feedback.synchronized_feedback.arm_command_feedback.arm_cartesian_feedback.status == arm_command_pb2.ArmCartesianCommand.Feedback.STATUS_TRAJECTORY_CANCELLED:
    +                return False
    +        elif arm_feedback.HasField("arm_gaze_feedback"):
    +            if arm_feedback.arm_gaze_feedback.status == arm_command_pb2.GazeCommand.Feedback.STATUS_TRAJECTORY_COMPLETE:
    +                return True
    +            elif arm_feedback.arm_gaze_feedback.status == arm_command_pb2.GazeCommand.Feedback.STATUS_TOOL_TRAJECTORY_STALLED:
    +                return False
    +        elif arm_feedback.HasField("arm_joint_move_feedback"):
    +            if arm_feedback.arm_joint_move_feedback.status == arm_command_pb2.ArmJointMoveCommand.Feedback.STATUS_COMPLETE:
    +                return True
    +        elif arm_feedback.HasField("named_arm_position_feedback"):
    +            if arm_feedback.named_arm_position_feedback.status == arm_command_pb2.NamedArmPositionsCommand.Feedback.STATUS_COMPLETE:
    +                return True
    +            elif arm_feedback.named_arm_position_feedback.status == arm_command_pb2.NamedArmPositionsCommand.Feedback.STATUS_STALLED_HOLDING_ITEM:
    +                return False
     
             time.sleep(0.1)
             now = time.time()
    diff --git a/docs/html/_modules/bosdyn/client/robot_id.html b/docs/html/_modules/bosdyn/client/robot_id.html
    index 71df2f560..bab4b819c 100644
    --- a/docs/html/_modules/bosdyn/client/robot_id.html
    +++ b/docs/html/_modules/bosdyn/client/robot_id.html
    @@ -7,7 +7,7 @@
       
       
       
    -  bosdyn.client.robot_id — Spot 2.3.5 documentation
    +  bosdyn.client.robot_id — Spot 3.0.0 documentation
       
     
       
    @@ -66,7 +66,7 @@
                 
                 
                   
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/client/robot_state.html b/docs/html/_modules/bosdyn/client/robot_state.html index fa76caad9..888ed089a 100644 --- a/docs/html/_modules/bosdyn/client/robot_state.html +++ b/docs/html/_modules/bosdyn/client/robot_state.html @@ -7,7 +7,7 @@ - bosdyn.client.robot_state — Spot 2.3.5 documentation + bosdyn.client.robot_state — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -693,6 +734,7 @@

    Source code for bosdyn.client.robot_state

     def _get_robot_link_model_value(response):
         return response.link_model
     
    +
     
    [docs]def has_arm(state_client, timeout=None): """Check if the robot has an arm attached. diff --git a/docs/html/_modules/bosdyn/client/sdk.html b/docs/html/_modules/bosdyn/client/sdk.html index 8293d3ef7..2b36d7f73 100644 --- a/docs/html/_modules/bosdyn/client/sdk.html +++ b/docs/html/_modules/bosdyn/client/sdk.html @@ -7,7 +7,7 @@ - bosdyn.client.sdk — Spot 2.3.5 documentation + bosdyn.client.sdk — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -566,6 +607,7 @@

    Source code for bosdyn.client.sdk

     
     from .arm_surface_contact import ArmSurfaceContactClient
     from .auth import AuthClient
    +from .auto_return import AutoReturnClient
     from .channel import DEFAULT_MAX_MESSAGE_LENGTH
     from .data_acquisition import DataAcquisitionClient
     from .data_acquisition_store import DataAcquisitionStoreClient
    @@ -580,11 +622,13 @@ 

    Source code for bosdyn.client.sdk

     from .exceptions import Error
     from .graph_nav import GraphNavClient
     from .image import ImageClient
    +from .ir_enable_disable import IREnableDisableServiceClient
     from .lease import LeaseClient
     from .license import LicenseClient
     from .log_annotation import LogAnnotationClient
     from .local_grid import LocalGridClient
     from .manipulation_api_client import ManipulationApiClient
    +from .map_processing import MapProcessingServiceClient
     from .network_compute_bridge_client import NetworkComputeBridgeClient
     from .payload import PayloadClient
     from .payload_registration import PayloadRegistrationClient
    @@ -600,6 +644,7 @@ 

    Source code for bosdyn.client.sdk

     from .time_sync import TimeSyncClient
     from .world_object import WorldObjectClient
     
    +
     
    [docs]class SdkError(Error): """General class of errors to handle non-response non-rpc errors."""
    @@ -641,6 +686,7 @@

    Source code for bosdyn.client.sdk

     _DEFAULT_SERVICE_CLIENTS = [
         ArmSurfaceContactClient,
         AuthClient,
    +    AutoReturnClient,
         DataAcquisitionClient,
         DataAcquisitionStoreClient,
         DataBufferClient,
    @@ -654,11 +700,13 @@ 

    Source code for bosdyn.client.sdk

         GraphNavClient,
         GraphNavRecordingServiceClient,
         ImageClient,
    +    IREnableDisableServiceClient,
         LeaseClient,
         LicenseClient,
         LogAnnotationClient,
         LocalGridClient,
         ManipulationApiClient,
    +    MapProcessingServiceClient,
         NetworkComputeBridgeClient,
         PayloadClient,
         PayloadRegistrationClient,
    @@ -812,11 +860,20 @@ 

    Source code for bosdyn.client.sdk

                     with open(cert_path, 'rb') as cert_file:
                         self.cert += cert_file.read()
    +
    [docs] def clear_robots(self): + """Remove all cached Robot instances. + Subsequent calls to create_robot() will return newly created Robots. + Existing robot instances will continue to work, but their time sync and token refresh + threads will be stopped. + """ + for robot in self.robots.values(): + robot._shutdown() + self.robots = {}
    +
    [docs] @staticmethod @deprecated( reason='App tokens are no longer in use. Authorization is now handled via licenses.', - version='2.0.1', - action="always") + version='2.0.1', action="always") def load_app_token(*_): """Do nothing, this method is kept only to maintain backwards compatibility.""" return
    diff --git a/docs/html/_modules/bosdyn/client/server_util.html b/docs/html/_modules/bosdyn/client/server_util.html index 958e81745..f4ae270fa 100644 --- a/docs/html/_modules/bosdyn/client/server_util.html +++ b/docs/html/_modules/bosdyn/client/server_util.html @@ -7,7 +7,7 @@ - bosdyn.client.server_util — Spot 2.3.5 documentation + bosdyn.client.server_util — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -551,13 +592,28 @@

    Source code for bosdyn.client.server_util

     # is subject to the terms and conditions of the Boston Dynamics Software
     # Development Kit License (20191101-BDSDK-SL).
     
    +"""Helper functions and classes for creating and running a gRPC service."""
    +
    +from concurrent import futures
    +import copy
    +import grpc
     import logging
    +import signal
    +import time
    +
     from bosdyn.api import header_pb2
    -import bosdyn.util
    +from bosdyn.api import data_acquisition_store_pb2
    +from bosdyn.api import data_buffer_pb2
    +from bosdyn.api import image_pb2
    +from bosdyn.api import local_grid_pb2
    +from bosdyn.api import log_annotation_pb2
    +from bosdyn.client.channel import generate_channel_options
     
    +import bosdyn.util
     
     _LOGGER = logging.getLogger(__name__)
     
    +
     
    [docs]class ResponseContext(object): """Helper to log gRPC request and response message to the data buffer for a service. @@ -571,19 +627,25 @@

    Source code for bosdyn.client.server_util

             request (protobuf): any gRPC request message with a bosdyn.api.RequestHeader proto.
             rpc_logger (DataBufferClient): Optional data buffer client to log the messages; if not
                 provided, only the headers will be mutated and nothing will be logged.
    +        channel_prefix (string): the prefix you want this req / resp pair logged under.
         """
     
    -    def __init__(self, response, request, rpc_logger=None):
    +    def __init__(self, response, request, rpc_logger=None, channel_prefix=None):
             self.response = response
             self.response.header.request_header.CopyFrom(request.header)
             self.request = request
             self.rpc_logger = rpc_logger
    +        self.channel_prefix = channel_prefix
     
         def __enter__(self):
             """Adds a start timestamp to the response header and logs the request RPC."""
             self.response.header.request_received_timestamp.CopyFrom(bosdyn.util.now_timestamp())
             if self.rpc_logger:
    -            self.rpc_logger.add_protobuf_async(self.request)
    +            if self.channel_prefix is None:
    +                channel = None
    +            else:
    +                channel = self.channel_prefix + "/" + self.request.DESCRIPTOR.full_name
    +            self.rpc_logger.add_protobuf_async(self.request, channel)
             return self.response
     
         def __exit__(self, exc_type, exc_val, exc_tb):
    @@ -596,8 +658,164 @@ 

    Source code for bosdyn.client.server_util

                 self.response.header.error.code = self.response.header.error.CODE_INTERNAL_SERVER_ERROR
                 self.response.header.error.message = "[%s] %s" % (exc_type.__name__, exc_val)
             if self.rpc_logger:
    -            self.rpc_logger.add_protobuf_async(self.response)
    + if self.channel_prefix is None: + channel = None + else: + channel = self.channel_prefix + "/" + self.response.DESCRIPTOR.full_name + self.rpc_logger.add_protobuf_async(self.response, channel)
    + + +
    [docs]class GrpcServiceRunner(object): + """A runner to start a gRPC server on a background thread and allow easy cleanup. + + Args: + service_servicer (custom servicer class derived from ServiceServicer): Servicer that + defines server behavior. + add_servicer_to_server_fn (function): Function generated by gRPC compilation that + attaches the servicer to the gRPC server. + port (int): The port number the service can be accessed through on the host system. + Defaults to 0, which will assign an ephemeral port. + max_send_message_length (int): Max message length (bytes) allowed for messages sent. + max_receive_message_length (int): Max message length (bytes) allowed for messages received. + timeout_secs (int): Number of seconds to wait for a clean server shutdown. + force_sigint_capture (bool): Re-assign the SIGINT handler to default in order to prevent + other scripts from blocking a clean exit. Defaults to True. + logger (logging.Logger): Logger to log with. + """ + + def __init__(self, service_servicer, add_servicer_to_server_fn, port=0, max_workers=4, + max_send_message_length=None, max_receive_message_length=None, timeout_secs=3, + force_sigint_capture=True, logger=None): + self.logger = logger or _LOGGER + self.timeout_secs = timeout_secs + self.force_sigint_capture = force_sigint_capture + + # Use the name of the service_servicer class for print messages. + self.server_type_name = type(service_servicer).__name__ + + self.server = grpc.server( + futures.ThreadPoolExecutor(max_workers=max_workers), + options=generate_channel_options(max_send_message_length, max_receive_message_length)) + add_servicer_to_server_fn(service_servicer, self.server) + self.port = self.server.add_insecure_port('[::]:{}'.format(port)) + self.server.start() + self.logger.info('Started the {} server.'.format(self.server_type_name)) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.stop() + +
    [docs] def stop(self): + """Blocks until the gRPC server shutsdown.""" + self.logger.info("Shutting down the {} server.".format(self.server_type_name)) + shutdown_complete = self.server.stop(None) + shutdown_complete.wait(self.timeout_secs)
    + +
    [docs] def run_until_interrupt(self): + """Spin the thread until a SIGINT is received and then shut down cleanly.""" + if self.force_sigint_capture: + # Ensure that KeyboardInterrupt is raised on a SIGINT. + signal.signal(signal.SIGINT, signal.default_int_handler) + + # Monitor for SIGINT and shut down cleanly. + try: + while True: + time.sleep(1) + except KeyboardInterrupt: + pass + self.stop()
    + + +
    [docs]def populate_response_header(response, request, error_code=header_pb2.CommonError.CODE_OK, + error_msg=None): + """Sets the ResponseHeader header in the response. + Args: + response (bosdyn.api Response message): The GRPC response message to be populated. + request (bosdyn.api Request message): The header from the request is added to the response. + error_code (header_pb2.CommonError): The status for the RPC response. + error_msg (str): An optional error message describing a bad header status failure. + Returns: + Mutates the response message's header to be fully populated. + """ + header = header_pb2.ResponseHeader() + header.request_received_timestamp.CopyFrom(bosdyn.util.now_timestamp()) + header.request_header.CopyFrom(request.header) + header.error.code = error_code + if error_msg: + header.error.message = error_msg + copied_request = copy.copy(request) + strip_large_bytes_fields(copied_request) + header.request.Pack(copied_request) + response.header.CopyFrom(header)
    + + +
    [docs]def strip_large_bytes_fields(proto_message): + """Removes any large bytes fields from a protobuf message depending on the proto type.""" + message_type = type(proto_message) + allowlist_map = get_bytes_field_allowlist() + if message_type in allowlist_map: + allowlist_map[message_type](proto_message)
    + + +
    [docs]def get_bytes_field_allowlist(): + """Creates set of protos which will have bytes fields removed.""" + allowlist_map = { + image_pb2.GetImageResponse: strip_get_image_response, + local_grid_pb2.GetLocalGridsResponse: strip_local_grid_responses, + data_acquisition_store_pb2.StoreDataRequest: strip_store_data_request, + data_acquisition_store_pb2.StoreImageRequest: strip_store_image_request, + data_buffer_pb2.RecordSignalTicksRequest: strip_record_signal_tick, + data_buffer_pb2.RecordDataBlobsRequest: strip_record_data_blob, + log_annotation_pb2.AddLogAnnotationRequest: strip_log_annotation + } + return allowlist_map
    + + +
    [docs]def strip_image_response(proto_message): + """Removes bytes from the image_pb2.ImageResponse proto.""" + proto_message.shot.image.ClearField("data")
    + + +
    [docs]def strip_get_image_response(proto_message): + """Removes bytes from the image_pb2.GetImageResponse proto.""" + for img_resp in proto_message.image_responses: + strip_image_response(img_resp)
    + + +
    [docs]def strip_local_grid_responses(proto_message): + """Removes bytes from the local_grid_pb2.GetLocalGridsResponse proto.""" + for grid_resp in proto_message.local_grid_responses: + grid_resp.local_grid.ClearField("data")
    + + +
    [docs]def strip_store_image_request(proto_message): + """Removes bytes from the data_acquisition_store_pb2.StoreImageRequest proto.""" + proto_message.image.image.ClearField("data")
    + + +
    [docs]def strip_store_data_request(proto_message): + """Removes bytes from the data_acquisition_store_pb2.StoreDataRequest proto.""" + proto_message.ClearField("data")
    + + +
    [docs]def strip_record_signal_tick(proto_message): + """Removes bytes from the data_buffer_pb2.RecordSignalTicksRequest proto.""" + for tick_data in proto_message.tick_data: + tick_data.ClearField("data")
    + + +
    [docs]def strip_record_data_blob(proto_message): + """Removes bytes from the data_buffer_pb2.RecordDataBlobsRequest proto.""" + for blob in proto_message.blob_data: + blob.ClearField("data")
    + +
    [docs]def strip_log_annotation(proto_message): + """Removes bytes from the log_annotation_pb2.AddLogAnnotationRequest proto.""" + for blob in proto_message.annotations.blob_data: + blob.ClearField("data")
    diff --git a/docs/html/_modules/bosdyn/client/spot_cam/audio.html b/docs/html/_modules/bosdyn/client/spot_cam/audio.html index 509945e78..c147feda3 100644 --- a/docs/html/_modules/bosdyn/client/spot_cam/audio.html +++ b/docs/html/_modules/bosdyn/client/spot_cam/audio.html @@ -7,7 +7,7 @@ - bosdyn.client.spot_cam.audio — Spot 2.3.5 documentation + bosdyn.client.spot_cam.audio — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -663,6 +704,76 @@

    Source code for bosdyn.client.spot_cam.audio

    return self.call(self._stub.LoadSound, yield_requests(data), self._load_sound_from_response,
                              self._audio_error_from_response, **kwargs)
    + # + # RPCs for Spot CAM+IR Only + # + +
    [docs] def set_audio_capture_channel(self, channel, **kwargs): + """Set the audio capture channel + + Args: + channel (audio_pb2.AudioCaptureChannel): Microphone to use + """ + request = audio_pb2.SetAudioCaptureChannelRequest(channel=channel) + return self.call(self._stub.SetAudioCaptureChannel, request, None, + self._audio_error_from_response, **kwargs)
    + +
    [docs] def set_audio_capture_channel_async(self, channel, **kwargs): + """Async version of set_audio_capture_channel()""" + request = audio_pb2.SetAudioCaptureChannelRequest(channel=channel) + return self.call_async(self._stub.SetAudioCaptureChannel, request, None, + self._audio_error_from_response, **kwargs)
    + +
    [docs] def get_audio_capture_channel(self, **kwargs): + """Retrieve the audio capture channel (microphone) + """ + request = audio_pb2.GetAudioCaptureChannelRequest() + return self.call(self._stub.GetAudioCaptureChannel, request, + self._get_audio_capture_channel_from_response, + self._audio_error_from_response, **kwargs)
    + +
    [docs] def get_audio_capture_channel_async(self, **kwargs): + """Async version of get_audio_capture_channel()""" + request = audio_pb2.GetAudioCaptureChannelRequest() + return self.call_async(self._stub.GetAudioCaptureChannel, request, + self._get_audio_capture_channel_from_response, + self._audio_error_from_response, **kwargs)
    + +
    [docs] def set_audio_capture_gain(self, channel, gain, **kwargs): + """Set the audio capture gain + + Args: + channel (audio_pb2.AudioCaptureChannel): Microphone to set gain for + gain (Double): Microphone gain, 0.0 to 1.0 + """ + request = audio_pb2.SetAudioCaptureGainRequest(channel=channel, gain=gain) + return self.call(self._stub.SetAudioCaptureGain, request, None, + self._audio_error_from_response, **kwargs)
    + +
    [docs] def set_audio_capture_gain_async(self, channel, gain, **kwargs): + """Async version of set_audio_capture_gain()""" + request = audio_pb2.SetAudioCaptureGainRequest(channel=channel, gain=gain) + return self.call_async(self._stub.SetAudioCaptureGain, request, None, + self._audio_error_from_response, **kwargs)
    + +
    [docs] def get_audio_capture_gain(self, channel, **kwargs): + """Retrieve the audio capture gain (microphone volume) + + Args: + channel (audio_pb2.AudioCaptureChannel): Microphone to get gain for + """ + request = audio_pb2.GetAudioCaptureGainRequest(channel=channel) + return self.call(self._stub.GetAudioCaptureGain, request, + self._get_audio_capture_gain_from_response, + self._audio_error_from_response, **kwargs)
    + +
    [docs] def get_audio_capture_gain_async(self, channel, **kwargs): + """Async version of get_audio_capture_gain()""" + request = audio_pb2.GetAudioCaptureGainRequest(channel=channel) + return self.call_async(self._stub.GetAudioCaptureGain, request, + self._get_audio_capture_gain_from_response, + self._audio_error_from_response, **kwargs)
    + @staticmethod def _list_sounds_from_response(response): return response.sounds @@ -687,6 +798,14 @@

    Source code for bosdyn.client.spot_cam.audio

    def _load_sound_from_response(response):
             pass
     
    +    @staticmethod
    +    def _get_audio_capture_channel_from_response(response):
    +        return response.channel
    +
    +    @staticmethod
    +    def _get_audio_capture_gain_from_response(response):
    +        return response.gain
    +
         @staticmethod
         @handle_common_header_errors
         def _audio_error_from_response(response):  # pylint: disable=unused-argument
    diff --git a/docs/html/_modules/bosdyn/client/spot_cam/compositor.html b/docs/html/_modules/bosdyn/client/spot_cam/compositor.html
    index cb10c3c9b..09118f060 100644
    --- a/docs/html/_modules/bosdyn/client/spot_cam/compositor.html
    +++ b/docs/html/_modules/bosdyn/client/spot_cam/compositor.html
    @@ -7,7 +7,7 @@
       
       
       
    -  bosdyn.client.spot_cam.compositor — Spot 2.3.5 documentation
    +  bosdyn.client.spot_cam.compositor — Spot 3.0.0 documentation
       
     
       
    @@ -66,7 +66,7 @@
                 
                 
                   
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -559,6 +600,7 @@

    Source code for bosdyn.client.spot_cam.compositor

    from google.protobuf.wrappers_pb2 import BoolValue +
    [docs]class CompositorClient(BaseClient): """A client calling Spot CAM Compositor services. """ diff --git a/docs/html/_modules/bosdyn/client/spot_cam/health.html b/docs/html/_modules/bosdyn/client/spot_cam/health.html index ef5628454..56294bba9 100644 --- a/docs/html/_modules/bosdyn/client/spot_cam/health.html +++ b/docs/html/_modules/bosdyn/client/spot_cam/health.html @@ -7,7 +7,7 @@ - bosdyn.client.spot_cam.health — Spot 2.3.5 documentation + bosdyn.client.spot_cam.health — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -561,6 +602,7 @@

    Source code for bosdyn.client.spot_cam.health

    from bosdyn.api.spot_cam import service_pb2_grpc from bosdyn.api.spot_cam import health_pb2 +
    [docs]class HealthClient(BaseClient): """A client calling Spot CAM Health service. """ @@ -579,7 +621,8 @@

    Source code for bosdyn.client.spot_cam.health

    [docs] def clear_bit_events_async(self, **kwargs): """Async version of clear_bit_events().""" request = health_pb2.ClearBITEventsRequest() - return self.call_async(self._stub.ClearBITEvents, request, self._clear_bit_events_from_response, + return self.call_async(self._stub.ClearBITEvents, request, + self._clear_bit_events_from_response, self._health_error_from_response, **kwargs)
    [docs] def get_bit_status(self, **kwargs): @@ -603,7 +646,8 @@

    Source code for bosdyn.client.spot_cam.health

    [docs] def get_temperature_async(self, **kwargs): """Async version of get_temperature().""" request = health_pb2.GetTemperatureRequest() - return self.call_async(self._stub.GetTemperature, request, self._get_temperature_from_response, + return self.call_async(self._stub.GetTemperature, request, + self._get_temperature_from_response, self._health_error_from_response, **kwargs)
    @staticmethod diff --git a/docs/html/_modules/bosdyn/client/spot_cam/lighting.html b/docs/html/_modules/bosdyn/client/spot_cam/lighting.html index 4dd7b2b96..8d3beffaa 100644 --- a/docs/html/_modules/bosdyn/client/spot_cam/lighting.html +++ b/docs/html/_modules/bosdyn/client/spot_cam/lighting.html @@ -7,7 +7,7 @@ - bosdyn.client.spot_cam.lighting — Spot 2.3.5 documentation + bosdyn.client.spot_cam.lighting — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/client/spot_cam/media_log.html b/docs/html/_modules/bosdyn/client/spot_cam/media_log.html index c9f91c794..94233b017 100644 --- a/docs/html/_modules/bosdyn/client/spot_cam/media_log.html +++ b/docs/html/_modules/bosdyn/client/spot_cam/media_log.html @@ -7,7 +7,7 @@ - bosdyn.client.spot_cam.media_log — Spot 2.3.5 documentation + bosdyn.client.spot_cam.media_log — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -557,6 +598,8 @@

    Source code for bosdyn.client.spot_cam.media_log

    _LOGGER = logging.getLogger(__name__) +from deprecated import deprecated + from bosdyn.client.common import (BaseClient, common_header_errors, handle_common_header_errors) from bosdyn.api.spot_cam import service_pb2_grpc from bosdyn.api.spot_cam import logging_pb2 @@ -586,7 +629,8 @@

    Source code for bosdyn.client.spot_cam.media_log

    return self.call_async(self._stub.Delete, request, self._delete_from_response, self._media_log_error_from_response, **kwargs)
    -
    [docs] def enable_debug(self, temp=False, humidity=False, bit=False, shock=True, system_stats=False, **kwargs): +
    [docs] def enable_debug(self, temp=False, humidity=False, bit=False, shock=True, system_stats=False, + **kwargs): """Start periodic logging of health data to the database, queryable via Health service. Args: @@ -595,21 +639,18 @@

    Source code for bosdyn.client.spot_cam.media_log

    bit: Enable logging of BIT events coming from the Health service. shock: Enable logging of Shock data. system_stats: Enable logging of cpu, gpu, memory, and network utilization.""" - request = logging_pb2.DebugRequest(enable_temperature=temp, - enable_humidity=humidity, - enable_BIT=bit, - enable_shock=shock, + request = logging_pb2.DebugRequest(enable_temperature=temp, enable_humidity=humidity, + enable_BIT=bit, enable_shock=shock, enable_system_stat=system_stats) return self.call(self._stub.EnableDebug, request, self._enable_debug_from_response, self._media_log_error_from_response, **kwargs)
    -
    [docs] def enable_debug_async(self, temp=False, humidity=False, bit=False, shock=True, system_stats=False, **kwargs): +
    [docs] def enable_debug_async(self, temp=False, humidity=False, bit=False, shock=True, + system_stats=False, **kwargs): """Async version of enable_debug()""" - request = logging_pb2.DebugRequest(enable_temperature=temp, - enable_humidity=humidity, - enable_BIT=bit, - enable_shock=shock, - enable_system_stat=system_stats) + request = logging_pb2.DebugRequest(enable_temperature=temp, enable_humidity=humidity, + enable_BIT=bit, enable_shock=shock, + enable_system_stat=system_stats) return self.call_async(self._stub.EnableDebug, request, self._enable_debug_from_response, self._media_log_error_from_response, **kwargs)
    @@ -666,16 +707,21 @@

    Source code for bosdyn.client.spot_cam.media_log

    return self.call(self._stub.RetrieveRawData, request, self._retrieve_from_response, self._media_log_error_from_response, **kwargs)
    -
    [docs] def set_passphrase(self, passphrase, **kwargs): +
    [docs] @deprecated(reason='Spot CAM encryption has been removed as a result of the switch to NTFS.', + version='3.0.0', action="always") + def set_passphrase(self, passphrase, **kwargs): """Set password for Spot CAM filesystem.""" request = logging_pb2.SetPassphraseRequest(passphrase=passphrase) return self.call(self._stub.SetPassphrase, request, self._set_passphrase_from_response, self._media_log_error_from_response, **kwargs)
    -
    [docs] def set_passphrase_async(self, passphrase, **kwargs): +
    [docs] @deprecated(reason='Spot CAM encryption has been removed as a result of the switch to NTFS.', + version='3.0.0', action="always") + def set_passphrase_async(self, passphrase, **kwargs): """Async version of set_passphrase()""" request = logging_pb2.SetPassphraseRequest(passphrase=passphrase) - return self.call_async(self._stub.SetPassphrase, request, self._set_passphrase_from_response, + return self.call_async(self._stub.SetPassphrase, request, + self._set_passphrase_from_response, self._media_log_error_from_response, **kwargs)
    [docs] def store(self, camera, record_type, tag=None, **kwargs): @@ -748,8 +794,8 @@

    Source code for bosdyn.client.spot_cam.media_log

    logpoint = response.logpoint chunk = response.data total += len(chunk.data) - _LOGGER.debug('Retrieved {} bytes ({}/{})'.format( - len(chunk.data), total, chunk.total_size)) + _LOGGER.debug('Retrieved {} bytes ({}/{})'.format(len(chunk.data), total, + chunk.total_size)) local_chunks.append(chunk) return logpoint, b''.join(chunk.data for chunk in local_chunks) diff --git a/docs/html/_modules/bosdyn/client/spot_cam/network.html b/docs/html/_modules/bosdyn/client/spot_cam/network.html index 098bedefb..4203f376a 100644 --- a/docs/html/_modules/bosdyn/client/spot_cam/network.html +++ b/docs/html/_modules/bosdyn/client/spot_cam/network.html @@ -7,7 +7,7 @@ - bosdyn.client.spot_cam.network — Spot 2.3.5 documentation + bosdyn.client.spot_cam.network — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -554,12 +595,13 @@

    Source code for bosdyn.client.spot_cam.network

    """For clients to the Spot CAM Network service.""" import socket -import struct +import struct from bosdyn.client.common import (BaseClient, handle_common_header_errors) from bosdyn.api.spot_cam import service_pb2_grpc from bosdyn.api.spot_cam import network_pb2 +

    [docs]class NetworkClient(BaseClient): """A client calling Spot CAM Network services such as ICE Candidates, SSL certs / Keys etc. diff --git a/docs/html/_modules/bosdyn/client/spot_cam/power.html b/docs/html/_modules/bosdyn/client/spot_cam/power.html index db7826fcd..7ce66d2ec 100644 --- a/docs/html/_modules/bosdyn/client/spot_cam/power.html +++ b/docs/html/_modules/bosdyn/client/spot_cam/power.html @@ -7,7 +7,7 @@ - bosdyn.client.spot_cam.power — Spot 2.3.5 documentation + bosdyn.client.spot_cam.power — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -563,6 +604,7 @@

    Source code for bosdyn.client.spot_cam.power

    from google.protobuf.wrappers_pb2 import BoolValue
     
    +
     
    [docs]class PowerClient(BaseClient): """A client calling Spot CAM Power service. """ @@ -581,7 +623,8 @@

    Source code for bosdyn.client.spot_cam.power

    [docs]    def get_power_status_async(self, **kwargs):
             """Async version of get_power_status()"""
             request = power_pb2.GetPowerStatusRequest()
    -        return self.call_async(self._stub.GetPowerStatus, request, self._get_power_status_from_response,
    +        return self.call_async(self._stub.GetPowerStatus, request,
    +                               self._get_power_status_from_response,
                                    self._power_error_from_response, **kwargs)
    [docs] def set_power_status(self, ptz=None, aux1=None, aux2=None, external_mic=None, **kwargs): @@ -599,7 +642,8 @@

    Source code for bosdyn.client.spot_cam.power

    """Async version of set_power_status()"""
             request = self._build_SetPowerStatusRequest(ptz, aux1, aux2, external_mic)
     
    -        return self.call_async(self._stub.SetPowerStatus, request, self._set_power_status_from_response,
    +        return self.call_async(self._stub.SetPowerStatus, request,
    +                               self._set_power_status_from_response,
                                    self._power_error_from_response, **kwargs)
    [docs] def cycle_power(self, ptz=None, aux1=None, aux2=None, external_mic=None, **kwargs): diff --git a/docs/html/_modules/bosdyn/client/spot_cam/ptz.html b/docs/html/_modules/bosdyn/client/spot_cam/ptz.html index b78167dc0..7f56d7e73 100644 --- a/docs/html/_modules/bosdyn/client/spot_cam/ptz.html +++ b/docs/html/_modules/bosdyn/client/spot_cam/ptz.html @@ -7,7 +7,7 @@ - bosdyn.client.spot_cam.ptz — Spot 2.3.5 documentation + bosdyn.client.spot_cam.ptz — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -689,7 +730,16 @@

    Source code for bosdyn.client.spot_cam.ptz

     
         @staticmethod
         def _initialize_lens_from_response(response):
    -        return response
    + return response + + # Focus methods + @staticmethod + def _get_ptz_focus_from_response(response): + return response.ptz_focus + + @staticmethod + def _set_ptz_focus_from_response(response): + return response.ptz_focus
    [docs]def shift_pan_angle(pan): """Shift the pan angle (degrees) so that it is in the [0,360] range.""" diff --git a/docs/html/_modules/bosdyn/client/spot_cam/streamquality.html b/docs/html/_modules/bosdyn/client/spot_cam/streamquality.html index 862ce8eda..721f36175 100644 --- a/docs/html/_modules/bosdyn/client/spot_cam/streamquality.html +++ b/docs/html/_modules/bosdyn/client/spot_cam/streamquality.html @@ -7,7 +7,7 @@ - bosdyn.client.spot_cam.streamquality — Spot 2.3.5 documentation + bosdyn.client.spot_cam.streamquality — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -561,6 +602,7 @@

    Source code for bosdyn.client.spot_cam.streamquality

    from bosdyn.api.spot_cam import service_pb2_grpc from bosdyn.api.spot_cam import streamquality_pb2 +
    [docs]class StreamQualityClient(BaseClient): """A client calling Spot CAM StreamQuality service. """ @@ -570,16 +612,20 @@

    Source code for bosdyn.client.spot_cam.streamquality

    def __init__(self): super(StreamQualityClient, self).__init__(service_pb2_grpc.StreamQualityServiceStub) -
    [docs] def set_stream_params(self, target_bitrate=None, refresh_interval=None, idr_interval=None, awb_mode=None, **kwargs): +
    [docs] def set_stream_params(self, target_bitrate=None, refresh_interval=None, idr_interval=None, + awb_mode=None, **kwargs): """Change image compression and postprocessing.""" - request = self._build_SetStreamParamsRequest(target_bitrate, refresh_interval, idr_interval, awb_mode) + request = self._build_SetStreamParamsRequest(target_bitrate, refresh_interval, idr_interval, + awb_mode) return self.call(self._stub.SetStreamParams, request, self._params_from_response, self._streamquality_error_from_response, **kwargs)
    -
    [docs] def set_stream_params_async(self, target_bitrate=None, refresh_interval=None, idr_interval=None, awb_mode=None, **kwargs): +
    [docs] def set_stream_params_async(self, target_bitrate=None, refresh_interval=None, idr_interval=None, + awb_mode=None, **kwargs): """Async version of set_stream_params().""" - request = self._build_SetStreamParamsRequest(target_bitrate, refresh_interval, idr_interval, awb_mode) + request = self._build_SetStreamParamsRequest(target_bitrate, refresh_interval, idr_interval, + awb_mode) return self.call_async(self._stub.SetStreamParams, request, self._params_from_response, self._streamquality_error_from_response, **kwargs)
    @@ -596,6 +642,18 @@

    Source code for bosdyn.client.spot_cam.streamquality

    return self.call_async(self._stub.GetStreamParams, request, self._params_from_response, self._streamquality_error_from_response, **kwargs)
    +
    [docs] def enable_congestion_control(self, enable=True, **kwargs): + """Enable congestion control.""" + request = streamquality_pb2.EnableCongestionControlRequest(enable_congestion_control=enable) + return self.call(self._stub.EnableCongestionControl, request, None, + self._streamquality_error_from_response, **kwargs)
    + +
    [docs] def enable_congestion_control_async(self, enable=True, **kwargs): + """Async version of enable_congestion_control().""" + request = streamquality_pb2.EnableCongestionControlRequest(enable_congestion_control=enable) + return self.call_async(self._stub.EnableCongestionControl, request, None, + self._streamquality_error_from_response, **kwargs)
    + @staticmethod def _build_SetStreamParamsRequest(target_bitrate, refresh_interval, idr_interval, awb_mode): request = streamquality_pb2.SetStreamParamsRequest() diff --git a/docs/html/_modules/bosdyn/client/spot_cam/version.html b/docs/html/_modules/bosdyn/client/spot_cam/version.html index 3c237abbe..f712d3339 100644 --- a/docs/html/_modules/bosdyn/client/spot_cam/version.html +++ b/docs/html/_modules/bosdyn/client/spot_cam/version.html @@ -7,7 +7,7 @@ - bosdyn.client.spot_cam.version — Spot 2.3.5 documentation + bosdyn.client.spot_cam.version — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/client/spot_check.html b/docs/html/_modules/bosdyn/client/spot_check.html index f3fb956e1..337432259 100644 --- a/docs/html/_modules/bosdyn/client/spot_check.html +++ b/docs/html/_modules/bosdyn/client/spot_check.html @@ -7,7 +7,7 @@ - bosdyn.client.spot_check — Spot 2.3.5 documentation + bosdyn.client.spot_check — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/client/time_sync.html b/docs/html/_modules/bosdyn/client/time_sync.html index 20874421d..5386695cf 100644 --- a/docs/html/_modules/bosdyn/client/time_sync.html +++ b/docs/html/_modules/bosdyn/client/time_sync.html @@ -7,7 +7,7 @@ - bosdyn.client.time_sync — Spot 2.3.5 documentation + bosdyn.client.time_sync — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -566,6 +607,7 @@

    Source code for bosdyn.client.time_sync

     from bosdyn.api.time_range_pb2 import TimeRange
     from bosdyn.util import (RobotTimeConverter, now_nsec, parse_timespan, nsec_to_timestamp,
                              set_timestamp_from_nsec, timestamp_to_nsec)
    +from google.protobuf import duration_pb2
     
     from .common import BaseClient, common_header_errors
     from .exceptions import Error
    @@ -682,8 +724,8 @@ 

    Source code for bosdyn.client.time_sync

                 return date_time.timestamp() * 1e9
             return None
     
    -    return robot_time_range_from_nanoseconds(
    -        _datetime_to_nsec(start_datetime), _datetime_to_nsec(end_datetime), time_sync_endpoint)
    + return robot_time_range_from_nanoseconds(_datetime_to_nsec(start_datetime), + _datetime_to_nsec(end_datetime), time_sync_endpoint)
    [docs]def timespec_to_robot_timespan(timespan_spec, time_sync_endpoint=None): @@ -705,6 +747,8 @@

    Source code for bosdyn.client.time_sync

         return robot_time_range_from_datetimes(start_datetime, end_datetime, time_sync_endpoint)
    + +
    [docs]class TimeSyncEndpoint: """A wrapper that uses a TimeSyncClient object to establish and maintain timesync with a robot. @@ -1055,8 +1099,8 @@

    Source code for bosdyn.client.time_sync

                 while not self.should_exit:
                     response = self._time_sync_endpoint.response
                     # pylint: disable=no-member
    -                if (not response or response.state.status ==
    -                        time_sync_pb2.TimeSyncState.STATUS_MORE_SAMPLES_NEEDED):
    +                if (not response or response.state.status
    +                        == time_sync_pb2.TimeSyncState.STATUS_MORE_SAMPLES_NEEDED):
                         # No wait between updates while time-sync is not established.
                         pass
                     elif response.state.status == time_sync_pb2.TimeSyncState.STATUS_SERVICE_NOT_READY:
    diff --git a/docs/html/_modules/bosdyn/client/token_cache.html b/docs/html/_modules/bosdyn/client/token_cache.html
    index c5577093b..290ae2c1a 100644
    --- a/docs/html/_modules/bosdyn/client/token_cache.html
    +++ b/docs/html/_modules/bosdyn/client/token_cache.html
    @@ -7,7 +7,7 @@
       
       
       
    -  bosdyn.client.token_cache — Spot 2.3.5 documentation
    +  bosdyn.client.token_cache — Spot 3.0.0 documentation
       
     
       
    @@ -66,7 +66,7 @@
                 
                 
                   
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/client/token_manager.html b/docs/html/_modules/bosdyn/client/token_manager.html index 0dd69818b..a8bc98909 100644 --- a/docs/html/_modules/bosdyn/client/token_manager.html +++ b/docs/html/_modules/bosdyn/client/token_manager.html @@ -7,7 +7,7 @@ - bosdyn.client.token_manager — Spot 2.3.5 documentation + bosdyn.client.token_manager — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -608,7 +649,8 @@

    Source code for bosdyn.client.token_manager

                     try:
                         self.robot.authenticate_with_token(self.robot.user_token)
                     except WriteFailedError:
    -                    _LOGGER.exception("Failed to save the token to the cache.  Continuing without caching.")
    +                    _LOGGER.exception(
    +                        "Failed to save the token to the cache.  Continuing without caching.")
                     except (InvalidTokenError, ResponseError, RpcError):
                         _LOGGER.exception("Error refreshing the token.  Retry in %s", retry_interval)
     
    diff --git a/docs/html/_modules/bosdyn/client/util.html b/docs/html/_modules/bosdyn/client/util.html
    index 249118bfa..5df8c48b9 100644
    --- a/docs/html/_modules/bosdyn/client/util.html
    +++ b/docs/html/_modules/bosdyn/client/util.html
    @@ -7,7 +7,7 @@
       
       
       
    -  bosdyn.client.util — Spot 2.3.5 documentation
    +  bosdyn.client.util — Spot 3.0.0 documentation
       
     
       
    @@ -66,7 +66,7 @@
                 
                 
                   
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -551,9 +592,11 @@

    Source code for bosdyn.client.util

     # is subject to the terms and conditions of the Boston Dynamics Software
     # Development Kit License (20191101-BDSDK-SL).
     
    +"""Helper functions and classes for creating client applications."""
    +
     from concurrent import futures
     import copy
    -from deprecated.sphinx import deprecated
    +from deprecated import deprecated
     import getpass
     import glob
     import grpc
    @@ -564,17 +607,10 @@ 

    Source code for bosdyn.client.util

     import time
     import threading
     
    -from bosdyn.api import header_pb2
    -from bosdyn.api import data_acquisition_store_pb2
    -from bosdyn.api import data_buffer_pb2
    -from bosdyn.api import image_pb2
    -from bosdyn.api import local_grid_pb2
    -from bosdyn.api import log_annotation_pb2
    +import bosdyn.client.server_util
     from bosdyn.client.channel import generate_channel_options
    -import bosdyn.util
    -
    -from .auth import InvalidLoginError
    -from .exceptions import Error
    +from bosdyn.client.auth import InvalidLoginError
    +from bosdyn.client.exceptions import Error
     import google.protobuf.descriptor
     
     _LOGGER = logging.getLogger(__name__)
    @@ -670,7 +706,8 @@ 

    Source code for bosdyn.client.util

             # main log already. If not, add it to a new handler.
             filter_exists = None
             for handler in logger.handlers:
    -            filter_exists = filter_exists or does_dedup_filter_exist(handler, always_print_logger_levels)
    +            filter_exists = filter_exists or does_dedup_filter_exist(handler,
    +                                                                     always_print_logger_levels)
             if not filter_exists:
                 dedupFilterLog = logging.StreamHandler()
                 # Propagate the filter through the handler. logging.Filter does not propagate to other
    @@ -692,7 +729,9 @@ 

    Source code for bosdyn.client.util

             Boolean indicating if the DedupLoggingMessages filter already exists and matches the new parameters.
         """
         for filt in logger.filters:
    -        if type(filt) == DedupLoggingMessages and filt.always_print_logger_levels == always_print_logger_levels:
    +        if type(
    +                filt
    +        ) == DedupLoggingMessages and filt.always_print_logger_levels == always_print_logger_levels:
                 return True
         return False
    @@ -701,14 +740,6 @@

    Source code for bosdyn.client.util

         return logging.getLogger()
    -
    [docs]@deprecated( - reason='App tokens are no longer in use. Authorization is now handled via licenses.', - version='2.0.1', - action="always") -def default_app_token_path(): - """Do nothing, this method is kept only to maintain backwards compatibility.""" - return
    -
    [docs]def add_base_arguments(parser): """Add hostname argument to parser. @@ -741,6 +772,7 @@

    Source code for bosdyn.client.util

         parser.add_argument('--guid', required=required, help='Unique GUID of the payload.')
         parser.add_argument('--secret', required=required, help='Secret of the payload.')
    +
    [docs]def add_service_hosting_arguments(parser): """Add arguments common to most applications hosting a GRPC service. @@ -752,6 +784,7 @@

    Source code for bosdyn.client.util

             ('The port number the service can be reached at (Warning: This port cannot be firewalled).'
              ' Defaults to 0, which will assign an ephemeral port'), type=int)
    +
    [docs]def add_service_endpoint_arguments(parser): """Add arguments common to most applications defining a GRPC service endpoint. @@ -764,7 +797,16 @@

    Source code for bosdyn.client.util

             ' e.g. "192.168.50.5"')
    -
    [docs]class GrpcServiceRunner(object): +
    [docs]@deprecated(reason='App tokens are no longer in use. Authorization is now handled via licenses.', + version='2.0.1', action="always") +def default_app_token_path(): + """Do nothing, this method is kept only to maintain backwards compatibility.""" + return
    + + +
    [docs]@deprecated(reason='The GrpcServiceRunner class helper has moved to server_util.py. Please use ' + 'bosdyn.client.server_util.GrpcServiceRunner.', version='3.0.0', action="always") +class GrpcServiceRunner(object): """A runner to start a gRPC server on a background thread and allow easy cleanup. Args: @@ -827,82 +869,60 @@

    Source code for bosdyn.client.util

     
     
     
    -
    [docs]def populate_response_header(response, request, error_code=header_pb2.CommonError.CODE_OK, - error_msg=None): - """Sets the ResponseHeader header in the response. - Args: - response (bosdyn.api Response message): The GRPC response message to be populated. - request (bosdyn.api Request message): The header from the request is added to the response. - error_code (header_pb2.CommonError): The status for the RPC response. - error_msg (str): An optional error message describing a bad header status failure. - Returns: - Mutates the response message's header to be fully populated. - """ - header = header_pb2.ResponseHeader() - header.request_received_timestamp.CopyFrom(bosdyn.util.now_timestamp()) - header.request_header.CopyFrom(request.header) - header.error.code = error_code - if error_msg: - header.error.message = error_msg - copied_request = copy.copy(request) - strip_large_bytes_fields(copied_request) - header.request.Pack(copied_request) - response.header.CopyFrom(header)
    - - -
    [docs]def strip_large_bytes_fields(proto_message): - message_type = type(proto_message) - whitelist_map = get_bytes_field_whitelist() - if message_type in whitelist_map: - whitelist_map[message_type](proto_message)
    - - -
    [docs]def get_bytes_field_whitelist(): - whitelist_map = { - image_pb2.GetImageResponse : strip_get_image_response, - local_grid_pb2.GetLocalGridsResponse : strip_local_grid_responses, - data_acquisition_store_pb2.StoreDataRequest : strip_store_data_request, - data_acquisition_store_pb2.StoreImageRequest : strip_store_image_request, - data_buffer_pb2.RecordSignalTicksRequest : strip_record_signal_tick, - data_buffer_pb2.RecordDataBlobsRequest : strip_record_data_blob, - log_annotation_pb2.AddLogAnnotationRequest : strip_log_annotation - } - return whitelist_map
    - - -
    [docs]def strip_image_response(proto_message): - proto_message.shot.image.ClearField("data")
    - -
    [docs]def strip_get_image_response(proto_message): - for img_resp in proto_message.image_responses: - strip_image_response(img_resp)
    - - -
    [docs]def strip_local_grid_responses(proto_message): - for grid_resp in proto_message.local_grid_responses: - grid_resp.local_grid.ClearField("data")
    - - -
    [docs]def strip_store_image_request(proto_message): - proto_message.image.image.ClearField("data")
    - -
    [docs]def strip_store_data_request(proto_message): - proto_message.ClearField("data")
    - - -
    [docs]def strip_record_signal_tick(proto_message): - for tick_data in proto_message.tick_data: - tick_data.ClearField("data")
    - - -
    [docs]def strip_record_data_blob(proto_message): - for blob in proto_message.blob_data: - blob.ClearField("data")
    - - -
    [docs]def strip_log_annotation(proto_message): - for blob in proto_message.annotations.blob_data: - blob.ClearField("data")
    +populate_response_header = deprecated( + reason='The populate_response_header helper has moved to ' + 'server_util.py. Please use bosdyn.client.server_util.populate_response_header.', + version='3.0.0', action="always")(bosdyn.client.server_util.populate_response_header) + +strip_large_bytes_fields = deprecated( + reason='The strip_large_bytes_fields helper has moved to ' + 'server_util.py. Please use bosdyn.client.server_util.strip_large_bytes_fields.', + version='3.0.0', action="always")(bosdyn.client.server_util.strip_large_bytes_fields) + +get_bytes_field_whitelist = deprecated( + reason='The get_bytes_field_whitelist helper has moved to ' + 'server_util.py. Please use bosdyn.client.server_util.get_bytes_field_allowlist.', + version='3.0.0', action="always")(bosdyn.client.server_util.get_bytes_field_allowlist) + +strip_image_response = deprecated( + reason='The strip_image_response helper has moved to ' + 'server_util.py. Please use bosdyn.client.server_util.strip_image_response.', version='3.0.0', + action="always")(bosdyn.client.server_util.strip_image_response) + +strip_get_image_response = deprecated( + reason='The strip_get_image_response helper has moved to ' + 'server_util.py. Please use bosdyn.client.server_util.strip_get_image_response.', + version='3.0.0', action="always")(bosdyn.client.server_util.strip_get_image_response) + +strip_local_grid_responses = deprecated( + reason='The strip_local_grid_responses helper has moved to ' + 'server_util.py. Please use bosdyn.client.server_util.strip_local_grid_responses.', + version='3.0.0', action="always")(bosdyn.client.server_util.strip_local_grid_responses) + +strip_store_image_request = deprecated( + reason='The strip_store_image_request helper has moved to ' + 'server_util.py. Please use bosdyn.client.server_util.strip_store_image_request.', + version='3.0.0', action="always")(bosdyn.client.server_util.strip_store_image_request) + +strip_store_data_request = deprecated( + reason='The strip_store_data_request helper has moved to ' + 'server_util.py. Please use bosdyn.client.server_util.strip_store_data_request.', + version='3.0.0', action="always")(bosdyn.client.server_util.strip_store_data_request) + +strip_record_signal_tick = deprecated( + reason='The strip_record_signal_tick helper has moved to ' + 'server_util.py. Please use bosdyn.client.server_util.strip_record_signal_tick.', + version='3.0.0', action="always")(bosdyn.client.server_util.strip_record_signal_tick) + +strip_record_data_blob = deprecated( + reason='The strip_record_data_blob helper has moved to ' + 'server_util.py. Please use bosdyn.client.server_util.strip_record_data_blob.', version='3.0.0', + action="always")(bosdyn.client.server_util.strip_record_data_blob) + +strip_log_annotation = deprecated( + reason='The strip_log_annotation helper has moved to ' + 'server_util.py. Please use bosdyn.client.server_util.strip_log_annotation.', version='3.0.0', + action="always")(bosdyn.client.server_util.strip_log_annotation)
    diff --git a/docs/html/_modules/bosdyn/client/world_object.html b/docs/html/_modules/bosdyn/client/world_object.html index a677afe50..3fc1426de 100644 --- a/docs/html/_modules/bosdyn/client/world_object.html +++ b/docs/html/_modules/bosdyn/client/world_object.html @@ -7,7 +7,7 @@ - bosdyn.client.world_object — Spot 2.3.5 documentation + bosdyn.client.world_object — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/geometry.html b/docs/html/_modules/bosdyn/geometry.html index 387df8e2d..8d1e3686a 100644 --- a/docs/html/_modules/bosdyn/geometry.html +++ b/docs/html/_modules/bosdyn/geometry.html @@ -7,7 +7,7 @@ - bosdyn.geometry — Spot 2.3.5 documentation + bosdyn.geometry — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/mission/client.html b/docs/html/_modules/bosdyn/mission/client.html index 8a8e3095f..da1ecb757 100644 --- a/docs/html/_modules/bosdyn/mission/client.html +++ b/docs/html/_modules/bosdyn/mission/client.html @@ -7,7 +7,7 @@ - bosdyn.mission.client — Spot 2.3.5 documentation + bosdyn.mission.client — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -558,7 +599,7 @@

    Source code for bosdyn.mission.client

     
     from google.protobuf import timestamp_pb2
     
    -from bosdyn.client.common import BaseClient
    +from bosdyn.client.common import BaseClient, handle_lease_use_result_errors
     from bosdyn.client.common import (common_header_errors, handle_common_header_errors,
                                       handle_unset_status_error, error_factory)
     
    @@ -691,6 +732,22 @@ 

    Source code for bosdyn.mission.client

             return self.call_async(self._stub.LoadMission, req, None, _load_mission_error_from_response,
                                    **kwargs)
    +
    [docs] def load_mission_as_chunks(self, root, leases, data_chunk_byte_size=1000*1000, **kwargs): + """Load a mission onto the robot. + Args: + root: Root node in a mission. + leases: All leases necessary to initialize a mission. + data_chunk_byte_size: max size of each streamed message + Raises: + RpcError: Problem communicating with the robot. + CompilationError: The mission failed to compile. + bosdyn.mission.client.ValidationError: The mission failed to validate. + """ + req = self._load_mission_request(root, leases) + return self.call(self._stub.LoadMissionAsChunks, + BaseClient.chunk_message(req, data_chunk_byte_size), None, + _load_mission_error_from_response, **kwargs)
    +
    [docs] def play_mission(self, pause_time_secs, leases, settings=None, **kwargs): """Play the loaded mission. @@ -755,6 +812,23 @@

    Source code for bosdyn.mission.client

             return self.call_async(self._stub.PauseMission, req, None,
                                    _pause_mission_error_from_response, **kwargs)
    +
    [docs] def stop_mission(self, **kwargs): + """Stop the running mission. + + Raises: + RpcError: Problem communicating with the robot. + NoMissionPlayingError: No mission playing. + """ + req = self._stop_mission_request() + return self.call(self._stub.StopMission, req, None, _stop_mission_error_from_response, + **kwargs)
    + +
    [docs] def stop_mission_async(self, **kwargs): + """Async version of stop_mission().""" + req = self._stop_mission_request() + return self.call_async(self._stub.StopMission, req, None, + _stop_mission_error_from_response, **kwargs)
    +
    [docs] def get_info(self, **kwargs): """Get static information about the loaded mission. @@ -825,6 +899,10 @@

    Source code for bosdyn.mission.client

         def _pause_mission_request():
             return mission_pb2.PauseMissionRequest()
     
    +    @staticmethod
    +    def _stop_mission_request():
    +        return mission_pb2.StopMissionRequest()
    +
         @staticmethod
         def _get_info_request():
             return mission_pb2.GetInfoRequest()
    @@ -847,12 +925,12 @@ 

    Source code for bosdyn.mission.client

     _ANSWER_QUESTION_STATUS_TO_ERROR = collections.defaultdict(lambda: (MissionResponseError, None))
     _ANSWER_QUESTION_STATUS_TO_ERROR.update({
         mission_pb2.AnswerQuestionResponse.STATUS_OK: (None, None),
    -    mission_pb2.AnswerQuestionResponse.STATUS_INVALID_QUESTION_ID: (InvalidQuestionId,
    -                                                                    InvalidQuestionId.__doc__),
    -    mission_pb2.AnswerQuestionResponse.STATUS_INVALID_CODE: (InvalidAnswerCode,
    -                                                             InvalidAnswerCode.__doc__),
    -    mission_pb2.AnswerQuestionResponse.STATUS_ALREADY_ANSWERED: (QuestionAlreadyAnswered,
    -                                                                 QuestionAlreadyAnswered.__doc__),
    +    mission_pb2.AnswerQuestionResponse.STATUS_INVALID_QUESTION_ID:
    +        (InvalidQuestionId, InvalidQuestionId.__doc__),
    +    mission_pb2.AnswerQuestionResponse.STATUS_INVALID_CODE:
    +        (InvalidAnswerCode, InvalidAnswerCode.__doc__),
    +    mission_pb2.AnswerQuestionResponse.STATUS_ALREADY_ANSWERED:
    +        (QuestionAlreadyAnswered, QuestionAlreadyAnswered.__doc__),
     })
     
     
    @@ -874,6 +952,7 @@ 

    Source code for bosdyn.mission.client

     
     @handle_common_header_errors
     @handle_unset_status_error(unset='STATUS_UNKNOWN')
    +@handle_lease_use_result_errors
     def _load_mission_error_from_response(response):
         return error_factory(response, response.status,
                              status_to_string=mission_pb2.LoadMissionResponse.Status.Name,
    @@ -889,6 +968,7 @@ 

    Source code for bosdyn.mission.client

     
     @handle_common_header_errors
     @handle_unset_status_error(unset='STATUS_UNKNOWN')
    +@handle_lease_use_result_errors
     def _play_mission_error_from_response(response):
         return error_factory(response, response.status,
                              status_to_string=mission_pb2.PlayMissionResponse.Status.Name,
    @@ -904,12 +984,29 @@ 

    Source code for bosdyn.mission.client

     
     @handle_common_header_errors
     @handle_unset_status_error(unset='STATUS_UNKNOWN')
    +@handle_lease_use_result_errors
     def _pause_mission_error_from_response(response):
         return error_factory(response, response.status,
                              status_to_string=mission_pb2.PauseMissionResponse.Status.Name,
                              status_to_error=_PAUSE_MISSION_STATUS_TO_ERROR)
     
     
    +_STOP_MISSION_STATUS_TO_ERROR = collections.defaultdict(lambda: (MissionResponseError, None))
    +_STOP_MISSION_STATUS_TO_ERROR.update({
    +    mission_pb2.StopMissionResponse.STATUS_OK: (None, None),
    +    mission_pb2.StopMissionResponse.STATUS_NO_MISSION_PLAYING: (NoMissionPlayingError, None),
    +})
    +
    +
    +@handle_common_header_errors
    +@handle_unset_status_error(unset='STATUS_UNKNOWN')
    +@handle_lease_use_result_errors
    +def _stop_mission_error_from_response(response):
    +    return error_factory(response, response.status,
    +                         status_to_string=mission_pb2.StopMissionResponse.Status.Name,
    +                         status_to_error=_STOP_MISSION_STATUS_TO_ERROR)
    +
    +
     _RESTART_MISSION_STATUS_TO_ERROR = collections.defaultdict(lambda: (MissionResponseError, None))
     _RESTART_MISSION_STATUS_TO_ERROR.update({
         mission_pb2.RestartMissionResponse.STATUS_OK: (None, None),
    @@ -920,6 +1017,7 @@ 

    Source code for bosdyn.mission.client

     
     @handle_common_header_errors
     @handle_unset_status_error(unset='STATUS_UNKNOWN')
    +@handle_lease_use_result_errors
     def _restart_mission_error_from_response(response):
         return error_factory(response, response.status,
                              status_to_string=mission_pb2.RestartMissionResponse.Status.Name,
    diff --git a/docs/html/_modules/bosdyn/mission/constants.html b/docs/html/_modules/bosdyn/mission/constants.html
    index 81e35241d..eda2048f5 100644
    --- a/docs/html/_modules/bosdyn/mission/constants.html
    +++ b/docs/html/_modules/bosdyn/mission/constants.html
    @@ -7,7 +7,7 @@
       
       
       
    -  bosdyn.mission.constants — Spot 2.3.5 documentation
    +  bosdyn.mission.constants — Spot 3.0.0 documentation
       
     
       
    @@ -66,7 +66,7 @@
                 
                 
                   
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/_modules/bosdyn/mission/exceptions.html b/docs/html/_modules/bosdyn/mission/exceptions.html index 1e21122ac..a462f6293 100644 --- a/docs/html/_modules/bosdyn/mission/exceptions.html +++ b/docs/html/_modules/bosdyn/mission/exceptions.html @@ -7,7 +7,7 @@ - bosdyn.mission.exceptions — Spot 2.3.5 documentation + bosdyn.mission.exceptions — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -561,11 +602,51 @@

    Source code for bosdyn.mission.exceptions

     
     
     
    [docs]class CompileError(Error): - """Error occurred during compilation."""
    + """Error occurred during compilation.""" + def __init__(self, msg='', node_proto=None): + Error.__init__(self, msg) + self.node_proto = node_proto + +
    [docs] def node_name(self): + """Returns the 'name' field of the Node proto if possible, None otherwise""" + if self.node_proto is None: + return None + try: + return self.node_proto.name + except Exception: + return None
    + +
    [docs] def node_impl(self): + """Returns the proto type of the Node 'impl' field if possible, None otherwise""" + if self.node_proto is None: + return None + try: + return self.node_proto.impl.TypeName() + except Exception: + return None
    + +
    [docs] def get_node_details(self): + """Get a string of the node detail.""" + details = '' + node_impl = self.node_impl() + if node_impl: + details = f' ({node_impl}' + + node_name = self.node_name() + if node_name is not None: + if not details: + details = ' (???' + details += f' with name "{node_name}")' + return details
    + + def __str__(self): + msg = super(CompileError, self).__str__() + return msg + self.get_node_details()
    [docs]class UnknownType(CompileError): - pass
    + def __str__(self): + return 'Do not know how to build {}'.format(self.node_impl())
    [docs]class ValidationError(Error): diff --git a/docs/html/_modules/bosdyn/mission/remote_client.html b/docs/html/_modules/bosdyn/mission/remote_client.html index 8140c8fa6..855dc26d5 100644 --- a/docs/html/_modules/bosdyn/mission/remote_client.html +++ b/docs/html/_modules/bosdyn/mission/remote_client.html @@ -7,7 +7,7 @@ - bosdyn.mission.remote_client — Spot 2.3.5 documentation + bosdyn.mission.remote_client — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -681,10 +722,10 @@

    Source code for bosdyn.mission.remote_client

    _ESTABLISH_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
     _ESTABLISH_STATUS_TO_ERROR.update({
         remote_pb2.EstablishSessionResponse.STATUS_OK: (None, None),
    -    remote_pb2.EstablishSessionResponse.STATUS_MISSING_LEASES: (MissingLeases,
    -                                                                MissingLeases.__doc__),
    -    remote_pb2.EstablishSessionResponse.STATUS_MISSING_INPUTS: (MissingInputs,
    -                                                                MissingInputs.__doc__),
    +    remote_pb2.EstablishSessionResponse.STATUS_MISSING_LEASES:
    +        (MissingLeases, MissingLeases.__doc__),
    +    remote_pb2.EstablishSessionResponse.STATUS_MISSING_INPUTS:
    +        (MissingInputs, MissingInputs.__doc__),
     })
     
     
    @@ -735,8 +776,8 @@ 

    Source code for bosdyn.mission.remote_client

    _TEARDOWN_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
     _TEARDOWN_STATUS_TO_ERROR.update({
         remote_pb2.TeardownSessionResponse.STATUS_OK: (None, None),
    -    remote_pb2.TeardownSessionResponse.STATUS_INVALID_SESSION_ID: (InvalidSessionId,
    -                                                                   InvalidSessionId.__doc__),
    +    remote_pb2.TeardownSessionResponse.STATUS_INVALID_SESSION_ID:
    +        (InvalidSessionId, InvalidSessionId.__doc__),
     })
     
     
    diff --git a/docs/html/_modules/bosdyn/mission/server_util.html b/docs/html/_modules/bosdyn/mission/server_util.html
    index db05f2349..4b17a05b2 100644
    --- a/docs/html/_modules/bosdyn/mission/server_util.html
    +++ b/docs/html/_modules/bosdyn/mission/server_util.html
    @@ -7,7 +7,7 @@
       
       
       
    -  bosdyn.mission.server_util — Spot 2.3.5 documentation
    +  bosdyn.mission.server_util — Spot 3.0.0 documentation
       
     
       
    @@ -66,7 +66,7 @@
                 
                 
                   
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -557,11 +598,9 @@

    Source code for bosdyn.mission.server_util

     from deprecated import deprecated
     
     
    -
    [docs]@deprecated( - reason='The ResponseContext helper class has moved to a common location. Please use ' - 'bosdyn.client.util.ResponseContext.', - version='2.3.5', - action="always") + +
    [docs]@deprecated(reason='The ResponseContext helper class has moved to a common location. Please use ' + 'bosdyn.client.server_util.ResponseContext.', version='2.3.5', action="always") class ResponseContext(object): def __init__(self, response, request, rpc_logger=None): @@ -583,16 +622,10 @@

    Source code for bosdyn.mission.server_util

                 self.rpc_logger.add_protobuf_async(self.response)
    -
    [docs]def set_response_header(response, request, error_code=header_pb2.CommonError.CODE_OK, - error_message=None): - """Sets the ResponseHeader header in the response.""" - header = header_pb2.ResponseHeader() - header.request_received_timestamp = bosdyn.util.now_timestamp() - header.request_header.CopyFrom(request.header) - header.error.code = error_code - if error_message: - header.error.message = error_message - response.header.CopyFrom(header)
    +set_response_header = deprecated( + reason='The bosdyn.mission.set_response_header helper class has moved to a common ' + 'location. Please use bosdyn.client.server_util.populate_response_header.', version='3.0.0', + action="always")(bosdyn.client.server_util.populate_response_header)
    diff --git a/docs/html/_modules/bosdyn/mission/util.html b/docs/html/_modules/bosdyn/mission/util.html index 7557ed6dc..03b85ae28 100644 --- a/docs/html/_modules/bosdyn/mission/util.html +++ b/docs/html/_modules/bosdyn/mission/util.html @@ -7,7 +7,7 @@ - bosdyn.mission.util — Spot 2.3.5 documentation + bosdyn.mission.util — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -561,6 +602,7 @@

    Source code for bosdyn.mission.util

     import google.protobuf.text_format
     
     from bosdyn.api.mission import mission_pb2, nodes_pb2, util_pb2
    +from bosdyn.api.docking import docking_pb2
     from bosdyn.api.graph_nav import graph_nav_pb2
     from bosdyn.mission import constants
     
    @@ -580,7 +622,8 @@ 

    Source code for bosdyn.mission.util

             self.destination_typename = destination_typename
     
         def __str__(self):
    -        return 'Could not convert "{}" to type "{}"'.format(self.original_value, self.destination_typename)
    + return 'Could not convert "{}" to type "{}"'.format(self.original_value, + self.destination_typename)
    _python_identifier_regex = re.compile('[A-Za-z_]\w*$') @@ -592,7 +635,8 @@

    Source code for bosdyn.mission.util

         if start_level == 0:
             string += '\n'
         prefix = '|' + '-' * start_level
    -    string += prefix + text(root) + (' ' if text(root) else '') + '(' + root.__class__.__name__ + ')'
    +    string += prefix + text(root) + (' '
    +                                     if text(root) else '') + '(' + root.__class__.__name__ + ')'
         if include_status:
             string += '\n' + prefix + 'Status code: [{}]'.format(root.last_result)
         for child in root.children:
    @@ -668,21 +712,22 @@ 

    Source code for bosdyn.mission.util

         if hasattr(inner_proto, 'children'):
             if num_children == 0:
                 raise Error('Proto "{}" of type "{}" has no children!'.format(node.name, inner_type))
    +        for child_tup in children:
    +            child_node = proto_from_tuple(child_tup)
    +            inner_proto.children.add().CopyFrom(child_node)
         elif hasattr(inner_proto, 'child'):
    -        if num_children != 1:
    +        if isinstance(inner_proto, nodes_pb2.ForDuration) and num_children == 2:
    +            inner_proto.child.CopyFrom(proto_from_tuple(children[0]))
    +            inner_proto.timeout_child.CopyFrom(proto_from_tuple(children[1]))
    +        elif num_children == 1:
    +            inner_proto.child.CopyFrom(proto_from_tuple(children[0]))
    +        else:
                 raise Error('Proto "{}" of type "{}" has {} children!'.format(
                     node.name, inner_type, num_children))
         elif num_children != 0:
             raise Error('Proto "{}" of type "{}" was given {} children, but I do not know how to add'
                         ' them!'.format(node.name, inner_type, num_children))
     
    -    for child_tup in children:
    -        child_node = proto_from_tuple(child_tup)
    -        if hasattr(inner_proto, 'children'):
    -            inner_proto.children.add().CopyFrom(child_node)
    -        elif hasattr(inner_proto, 'child'):
    -            inner_proto.child.CopyFrom(child_node)
    -
         node.impl.Pack(inner_proto)
         return node
    @@ -796,7 +841,9 @@

    Source code for bosdyn.mission.util

             raise InvalidConversion(result, util_pb2.Result.DESCRIPTOR.full_name)
    -
    [docs]def most_restrictive_travel_params(travel_params, vel_limit=None): +
    [docs]def most_restrictive_travel_params(travel_params, vel_limit=None, + disable_directed_exploration=False, + disable_alternate_route_finding=False): if travel_params is None: travel_params = graph_nav_pb2.TravelParams() else: @@ -828,6 +875,10 @@

    Source code for bosdyn.mission.util

     
         if vel_limit is not None:
             take_velocity_limit(travel_params.velocity_limit, vel_limit)
    +
    +    travel_params.disable_directed_exploration = travel_params.disable_directed_exploration or disable_directed_exploration
    +    travel_params.disable_alternate_route_finding = travel_params.disable_alternate_route_finding or disable_alternate_route_finding
    +
         return travel_params
    diff --git a/docs/html/_modules/bosdyn/util.html b/docs/html/_modules/bosdyn/util.html index f86ebba48..29039b788 100644 --- a/docs/html/_modules/bosdyn/util.html +++ b/docs/html/_modules/bosdyn/util.html @@ -7,7 +7,7 @@ - bosdyn.util — Spot 2.3.5 documentation + bosdyn.util — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -553,13 +594,14 @@

    Source code for bosdyn.util

     
     """Common utilities for API Python code."""
     from __future__ import division
    +
    +import datetime
     import re
     import sys
     import time
    -import datetime
     
    -from google.protobuf.timestamp_pb2 import Timestamp
     from google.protobuf.duration_pb2 import Duration
    +from google.protobuf.timestamp_pb2 import Timestamp
     
     if sys.version_info[0] >= 3:
         LONG = int
    @@ -651,11 +693,27 @@ 

    Source code for bosdyn.util

         return LONG(secs * NSEC_PER_SEC)
    +
    [docs]def nsec_to_sec(secs): + """Convert time in nanoseconds to a timestamp in seconds. + + Args: + secs: Time in nanoseconds + Returns: + The time in seconds, as a . + """ + return float(secs) / NSEC_PER_SEC
    + +
    [docs]def now_nsec(): """Returns nanoseconds from dawn of unix epoch until when this is called.""" return sec_to_nsec(time.time())
    +
    [docs]def now_sec(): + """Returns seconds from dawn of unix epoch until when this is called.""" + return time.time()
    + +
    [docs]def set_timestamp_from_now(timestamp_proto): """Sets google.protobuf.Timestamp to point to the current time on the system clock. @@ -835,7 +893,16 @@

    Source code for bosdyn.util

     
     
     
    [docs]def parse_timespan(timespan_spec): - """Parse the timespan.""" + """Parse a timespan spec of the form {from-time}[-{to-time}] + + Args: + val: string with format {spec} or {spec}-{spec} where {spec} is a string + with a format as described by TIME_FORMAT_DESC. + + Returns: (datetime.datetime, None) or (datetime.datetime, datetime.datetime). + + Raises: DatetimeParseError if format of val is not recognized. + """ dash_idx = timespan_spec.find('-') if dash_idx < 0: return parse_datetime(timespan_spec), None @@ -882,7 +949,15 @@

    Source code for bosdyn.util

             Args:
               timestamp_proto[in/out] (google.protobuf.Timestamp): local system time time
             """
    -        timestamp_proto.CopyFrom(self.robot_timestamp_from_local(timestamp_proto))
    + timestamp_proto.CopyFrom(self.robot_timestamp_from_local(timestamp_proto))
    + +
    [docs] def robot_seconds_from_local_seconds(self, local_time_secs): + """Returns the robot time in seconds from a local time in seconds. + + Args: + local_time_secs: Local system time time, in seconds from the unix epoch. + """ + return local_time_secs + nsec_to_sec(self._clock_skew_nsec)
    diff --git a/docs/html/_modules/index.html b/docs/html/_modules/index.html index 6ad29f7ef..d5fcfac3f 100644 --- a/docs/html/_modules/index.html +++ b/docs/html/_modules/index.html @@ -7,7 +7,7 @@ - Overview: module code — Spot 2.3.5 documentation + Overview: module code — Spot 3.0.0 documentation @@ -66,7 +66,7 @@
    - 2.3.5 + 3.0.0
    @@ -99,6 +99,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -242,6 +268,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -265,12 +292,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -318,6 +347,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -341,6 +371,8 @@
  • Choreography
  • @@ -374,6 +406,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -400,6 +434,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -407,6 +443,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -426,6 +464,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -443,6 +482,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -460,6 +500,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -554,16 +595,20 @@

    All modules for which code is available

  • bosdyn.bddf.grpc_reader
  • bosdyn.bddf.grpc_service_reader
  • bosdyn.bddf.grpc_service_writer
  • +
  • bosdyn.bddf.message_reader
  • bosdyn.bddf.pod_series_reader
  • bosdyn.bddf.pod_series_writer
  • bosdyn.bddf.protobuf_channel_reader
  • bosdyn.bddf.protobuf_reader
  • bosdyn.bddf.protobuf_series_writer
  • bosdyn.bddf.stream_data_reader
  • +
  • bosdyn.choreography.client.animation_file_conversion_helpers
  • +
  • bosdyn.choreography.client.animation_file_to_proto
  • bosdyn.choreography.client.choreography
  • bosdyn.client.arm_surface_contact
  • bosdyn.client.async_tasks
  • bosdyn.client.auth
  • +
  • bosdyn.client.auto_return
  • bosdyn.client.bddf_download
  • bosdyn.client.channel
  • bosdyn.client.command_line
  • @@ -586,11 +631,13 @@

    All modules for which code is available

  • bosdyn.client.graph_nav
  • bosdyn.client.image
  • bosdyn.client.image_service_helpers
  • +
  • bosdyn.client.ir_enable_disable
  • bosdyn.client.lease
  • bosdyn.client.license
  • bosdyn.client.local_grid
  • bosdyn.client.log_annotation
  • bosdyn.client.manipulation_api_client
  • +
  • bosdyn.client.map_processing
  • bosdyn.client.math_helpers
  • bosdyn.client.network_compute_bridge_client
  • bosdyn.client.payload
  • diff --git a/docs/html/_static/documentation_options.js b/docs/html/_static/documentation_options.js index 2e7376a8d..8d2a4675e 100644 --- a/docs/html/_static/documentation_options.js +++ b/docs/html/_static/documentation_options.js @@ -1,6 +1,6 @@ var DOCUMENTATION_OPTIONS = { URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), - VERSION: '2.3.5', + VERSION: '3.0.0', LANGUAGE: 'None', COLLAPSE_INDEX: false, BUILDER: 'html', diff --git a/docs/html/choreography_protos/bosdyn/api/README.html b/docs/html/choreography_protos/bosdyn/api/README.html index 248c1cdd1..4af3aab6d 100644 --- a/docs/html/choreography_protos/bosdyn/api/README.html +++ b/docs/html/choreography_protos/bosdyn/api/README.html @@ -8,7 +8,7 @@ - Choreography Protos — Spot 2.3.5 documentation + Choreography Protos — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/choreography_protos/bosdyn/api/choreography_reference.html b/docs/html/choreography_protos/bosdyn/api/choreography_reference.html index c8c3d9e54..4ec112039 100644 --- a/docs/html/choreography_protos/bosdyn/api/choreography_reference.html +++ b/docs/html/choreography_protos/bosdyn/api/choreography_reference.html @@ -8,7 +8,7 @@ - spot/choreography_params.proto — Spot 2.3.5 documentation + spot/choreography_params.proto — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -555,7 +596,116 @@

    spot/choreography_params.proto

    -

    +

    +
    +

    AnimateParams

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    animation_namestringThe name of the animated move. There are no default values/bounds associated with this field.
    body_entry_slicesgoogle.protobuf.DoubleValueHow many slices to smoothly transition from previous pose to animation.
    body_exit_slicesgoogle.protobuf.DoubleValueHow many slices to return from animation to nominal pose. Zero indicates to keep final animated pose.
    translation_multiplierbosdyn.api.Vec3ValueMultiplier for animated translation by axis to exaggerate or suppress motion along specific axes.
    rotation_multiplierEulerZYXValueMultiplier for the animated orientation by axis to exaggerate or suppress motion along specific axes.
    arm_entry_slicesgoogle.protobuf.DoubleValueHow many slices to smoothly transition from previous pose to animation.
    shoulder_0_offsetgoogle.protobuf.DoubleValueJoint angle offsets in radians for the arm joints.
    shoulder_1_offsetgoogle.protobuf.DoubleValue
    elbow_0_offsetgoogle.protobuf.DoubleValue
    elbow_1_offsetgoogle.protobuf.DoubleValue
    wrist_0_offsetgoogle.protobuf.DoubleValue
    wrist_1_offsetgoogle.protobuf.DoubleValue
    gripper_offsetgoogle.protobuf.DoubleValue
    speedgoogle.protobuf.DoubleValueHow fast to playback. 1.0 is normal speed. larger is faster.
    offset_slicesgoogle.protobuf.DoubleValueHow late into the nominal script to start.
    gripper_multipliergoogle.protobuf.DoubleValueMultiply all gripper angles by this value.
    gripper_strength_fractiongoogle.protobuf.DoubleValueHow hard the gripper can squeeze. Fraction of full strength.
    arm_dance_frame_idgoogle.protobuf.Int32ValueWhich dance frame to use as a reference for workspace arm moves. Including this parameter overrides the animation frame.
    body_tracking_stiffnessgoogle.protobuf.DoubleValueHow hard to try to track the animated body motion. Only applicable to animations that control both the body and the legs. On a scale of 1 to 10 (11 for a bit extra). Higher will result in more closely tracking the animated body motion, but possibly at the expense of balance for more difficult animations.

    +

    ArmMoveParams

    Parameters specific to ArmMove move.

    @@ -784,6 +934,35 @@

    ClapParams

    +

    +
    +

    Color

    + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    redgoogle.protobuf.DoubleValue
    greengoogle.protobuf.DoubleValue
    bluegoogle.protobuf.DoubleValue

    @@ -879,10 +1058,10 @@

    EulerZYXValue

    +

    -
    -

    Figure8Params

    +
    +

    FadeColorParams

    @@ -893,26 +1072,30 @@

    Figure8Paramsgoogle.protobuf.DoubleValue +

    + - + + + + + + - + -
    top_colorColor
    widthbottom_colorColor
    fade_in_slices google.protobuf.DoubleValue
    beats_per_cyclefade_out_slices google.protobuf.DoubleValue

    +

    -
    -

    FrontUpParams

    -

    Parameters specific to FrontUp move.

    +
    +

    FidgetStandParams

    @@ -923,126 +1106,80 @@

    FrontUpParamsgoogle.protobuf.BoolValue -

    - - -
    Should we raise the hind feet instead.

    -
    -
    -

    GripperParams

    -

    Parameters for open/close of gripper.

    - - - - - - + + + - - - + - + - + - + - -
    FieldTypeDescriptionpresetFidgetStandParams.FidgetPreset
    anglemin_gaze_pitch google.protobuf.DoubleValueAngle in radians at which the gripper is open. Note that a 0 radian angle correlates to completely closed.
    speedmax_gaze_pitch google.protobuf.DoubleValueSpeed in m/s at which the gripper should open/close to achieve the desired angle.

    -
    -
    -

    HopParams

    -

    Parameters specific to Hop move.

    - - - - - + + + - - - - - + + + - + - + - + - - - -
    FieldTypeDescriptiongaze_mean_periodgoogle.protobuf.DoubleValue
    velocitybosdyn.api.Vec2ValueThe velocity of the hop gait (X is forward; y is left)[m/s].gaze_center_cfpbosdyn.api.Vec3Value
    yaw_rateshift_mean_period google.protobuf.DoubleValueHow fast the hop gait should turn [rad/s].
    stand_timeshift_max_transition_time google.protobuf.DoubleValueHow long the robot should stand in between each hop.

    -
    -
    -

    JumpParams

    -

    Parameters for the robot making a jump.

    - - - - - - + - - - + - + - + - + - + - + - + - + - - - + + + - - - + + + - + - - - - - -
    FieldTypeDescription
    yawbreath_min_z google.protobuf.DoubleValueThe amount in radians that the robot will turn while in the air.
    flight_slicesbreath_max_z google.protobuf.DoubleValueThe amount of time in slices (beats) that the robot will be in the air.
    stance_widthbreath_max_period google.protobuf.DoubleValueThe distance between the robot's left and right feet [m].
    stance_lengthleg_gesture_mean_period google.protobuf.DoubleValueThe distance between the robot's front and back feet [m].
    absolutegoogle.protobuf.BoolValueShould we turn to a yaw in choreography sequence frame?gaze_slew_rategoogle.protobuf.DoubleValue
    translationbosdyn.api.Vec2ValueHow far the robot should jump [m].gaze_position_generation_gainbosdyn.api.Vec3Value
    split_fractiongaze_roll_generation_gain google.protobuf.DoubleValueHow much it should lo/td the first pair of lets ahead of the other pair. In fraction of flight time.
    lead_leg_pairJumpParams.Lead

    +

    -
    -

    KneelCircleParams

    -

    Parameters specific to the kneel_circles move.

    +
    +

    Figure8Params

    @@ -1053,41 +1190,25 @@

    KneelCircleParamsbosdyn.api.Vec3Value -

    - - - - - - - - + - + - + - + - + - - - - - - + -
    Location in body frame of the circle center. A typical value for the location is (0.4, 0, -0.5).
    beats_per_circlegoogle.protobuf.Int32ValueHow beats per circle. One or two are reasonable values.
    number_of_circlesheight google.protobuf.DoubleValueHow many circles to perform. Mutually exclusive with beats_per_circle.
    offsetwidth google.protobuf.DoubleValueHow far apart the feet are when circling [m].
    radiusbeats_per_cycle google.protobuf.DoubleValueSize of the circles [m].
    reversegoogle.protobuf.BoolValueWhich way to circle.

    +

    -
    -

    KneelLegMove2Params

    -

    Parameters specific to KneelLegMove2 move.

    +
    +

    FrameSnapshotParams

    @@ -1098,51 +1219,46 @@

    KneelLegMove2Paramsgoogle.protobuf.DoubleValue -

    + + + - - + + - - + + - - - - - - - + + - - + + - - - + + + - + - + -
    Joint angles of the front left leg in radians.frame_idgoogle.protobuf.Int32Value
    left_hip_ygoogle.protobuf.DoubleValuefiducial_numbergoogle.protobuf.Int32Value
    left_kneegoogle.protobuf.DoubleValueinclude_front_left_legFrameSnapshotParams.Inclusion
    right_hip_xgoogle.protobuf.DoubleValueJoint angles of the front right leg in radians.
    right_hip_ygoogle.protobuf.DoubleValueinclude_front_right_legFrameSnapshotParams.Inclusion
    right_kneegoogle.protobuf.DoubleValueinclude_hind_left_legFrameSnapshotParams.Inclusion
    easingEasingHow the motion should be paced.include_hind_right_legFrameSnapshotParams.Inclusion
    link_to_nextcompensated google.protobuf.BoolValueShould we combine with the next move into a smooth trajectory.

    +

    -
    -

    KneelLegMoveParams

    -

    Parameters specific to KneelLegMove move.

    +
    +

    FrontUpParams

    +

    Parameters specific to FrontUp move.

    @@ -1153,36 +1269,15 @@

    KneelLegMoveParamsgoogle.protobuf.DoubleValue -

    - - - - - - - - - - - - - - - - - - + -
    Joint angles of the left front leg in radians. If mirrored, the joints will be flipped for the other leg.
    hip_ygoogle.protobuf.DoubleValue
    kneegoogle.protobuf.DoubleValue
    mirror google.protobuf.BoolValueIf mirrored is true, the joints will be flipped for the leg on the other side (right vs left) of the body.
    easingEasingHow the motion should be paced.Should we raise the hind feet instead.

    +

    -
    -

    Pace2StepParams

    -

    Parameters specific to pace translation.

    +
    +

    GotoParams

    @@ -1193,31 +1288,36 @@

    Pace2StepParamsbosdyn.api.Vec2Value -

    + - + - + - + - + + + + + + - + -
    Where to move.
    swing_heightabsolute_yaw google.protobuf.DoubleValueSwing parameters to describe the footstep pattern during the pace translation gait. Note, a zero (or nearly zero) value will be considered as an unspecified parameter.
    swing_velocitystep_position_stiffness google.protobuf.DoubleValue
    absoluteduty_cyclegoogle.protobuf.DoubleValue
    link_to_next google.protobuf.BoolValueShould the motion be relative to where the dance started (true) rather than relative to the current position (false).Should we combine with the next move into a smooth trajectory.

    +

    -
    -

    RandomRotateParams

    -

    Parameters specific to the RandomRotate move.

    +
    +

    GripperParams

    +

    Parameters for open/close of gripper.

    @@ -1228,36 +1328,21 @@

    RandomRotateParamsEulerZYXValue -

    - - - - - - - - + - - - - - - + - + - + -
    The amplitude [rad] of the rotation in each axis.
    speedEulerRateZYXValueThe speed [rad/s] of the motion in each axis.
    speed_variationangle google.protobuf.DoubleValueThe amount of variation allowed in the speed of the random rotations [m/s]. Note, this must be a positive value.
    num_speed_tiersgoogle.protobuf.Int32ValueThe specified speed values will be split into this many number of tiers between the bounds of [speed - speed_variation, speed + speed variation]. Then a tier (with a specified speed) will be randomly choosen and performed for each axis.Angle in radians at which the gripper is open. Note that a 0 radian angle correlates to completely closed.
    tier_variationspeed google.protobuf.DoubleValueHow much can the output speed vary from the choosen tiered speed.Speed in m/s at which the gripper should open/close to achieve the desired angle.

    +

    -
    -

    RotateBodyParams

    -

    Parameters for the robot rotating the body.

    +
    +

    HopParams

    +

    Parameters specific to Hop move.

    @@ -1268,21 +1353,25 @@

    RotateBodyParamsEulerZYXValue -

    + + + - - - + + + + + + + + -
    The robot will rotate its body to the specified orientation (roll/pitch/yaw).velocitybosdyn.api.Vec2ValueThe velocity of the hop gait (X is forward; y is left)[m/s].
    return_to_start_posegoogle.protobuf.BoolValueIf true, the robot will transition back to the initial pose we started at before this choreography sequence move begin execution, and otherwise it will remain in whatever pose it is in after completing the choreography sequence move.yaw_rategoogle.protobuf.DoubleValueHow fast the hop gait should turn [rad/s].
    stand_timegoogle.protobuf.DoubleValueHow long the robot should stand in between each hop.

    +

    -
    -

    RunningManParams

    -

    Parameters specific to RunningMan move.

    +
    +

    IndependentColorParams

    @@ -1293,51 +1382,61 @@

    RunningManParamsbosdyn.api.Vec2Value +

    + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - + + + + + + + + + + + - + - + - + -
    top_leftColor
    swing_heightgoogle.protobuf.DoubleValueHow high to pick up the forward-moving feet [m].upper_mid_leftColor
    spreadgoogle.protobuf.DoubleValueHow far to spread the contralateral pair of feet [m].lower_mid_leftColor
    reversegoogle.protobuf.BoolValueShould we reverse the motion?bottom_leftColor
    pre_move_cyclesgoogle.protobuf.Int32ValueHow many full running man cycles should the robot complete in place before starting to move with the desired velocity.top_rightColor
    speed_multipliergoogle.protobuf.DoubleValueDo the move at some multiple of the dance cadence.upper_mid_rightColor
    duty_cyclelower_mid_rightColor
    bottom_rightColor
    fade_in_slices google.protobuf.DoubleValueWhat fraction of the time to have feet on the ground.
    com_heightfade_out_slices google.protobuf.DoubleValueHow high to hold the center of mass above the ground on average.

    +

    -
    -

    SideParams

    -

    Parameters for moves that can go to either side.

    +
    +

    JumpParams

    +

    Parameters for the robot making a jump.

    @@ -1348,64 +1447,76 @@

    SideParamsSideParams.Side -

    + + + - -
    yawgoogle.protobuf.DoubleValueThe amount in radians that the robot will turn while in the air.

    -
    -
    -

    StepParams

    - - - - - + + + - - - - - + + + - + + + + + + - + - - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + -
    FieldTypeDescriptionflight_slicesgoogle.protobuf.DoubleValueThe amount of time in slices (beats) that the robot will be in the air.
    footLegWhich foot to use (FL = 1, FR = 2, HL = 3, HR = 4).stance_widthgoogle.protobuf.DoubleValueThe distance between the robot's left and right feet [m].
    offsetstance_lengthgoogle.protobuf.DoubleValueThe distance between the robot's front and back feet [m].
    translation bosdyn.api.Vec2ValueOffset of the foot from it's nominal position, in meters.How far the robot should jump [m].
    second_footLegShould we use a second foot? (None = 0, FL = 1, FR = 2, HL = 3, HR = 4).split_fractiongoogle.protobuf.DoubleValueHow much it should lo/td the first pair of lets ahead of the other pair. In fraction of flight time.
    swing_waypointbosdyn.api.Vec3ValueWhere should the swing foot go? This vector should be described in a gravity-aligned body frame relative to the centerpoint of the swing. If set to {0,0,0}, uses the default swing path.lead_leg_pairJumpParams.Lead
    yaw_is_absolutegoogle.protobuf.BoolValueShould we turn to a yaw in choreography sequence frame?
    translation_is_absolutegoogle.protobuf.BoolValueShould we translate in choreography sequence frame?
    absolute_yawgoogle.protobuf.DoubleValueThe direction the robot should face upon landing relative to pose at the start of the dance. [rad]
    absolute_translationbosdyn.api.Vec2ValueWhere the robot should land relative to the pose at the start of the dance. [m]
    swing_height google.protobuf.DoubleValueParameters for altering swing. Note that these will have no effect if swing_waypoint is specified. As well, a zero (or nearly zero) value will be considered as an unspecified parameter.
    absolutegoogle.protobuf.BoolValueDeprecation Warning *** DEPRECATED as of 3.0.0: The absolute field has been deprecated and split into the yaw_is_absolute and translation_is_absolute fields. The following field will be deprecated and moved to 'reserved' in a future release.

    meters | -| liftoff_velocity | google.protobuf.DoubleValue | m/s | -| touchdown_velocity | google.protobuf.DoubleValue | m/s | -| mirror_x | google.protobuf.BoolValue | Should we mirror the offset for the second foot? Ignored if second_foot is set to None | -| mirror_y | google.protobuf.BoolValue | | -| mirror | google.protobuf.BoolValue | Deprecation Warning *** DEPRECATED as of 2.3.0: The mirror field has been deprecated in favor for a more descriptive break down to mirror_x and mirror_y. The following field will be deprecated and moved to ‘reserved’ in a future release. | -| waypoint_dwell | google.protobuf.DoubleValue | What fraction of the swing should be spent near the waypoint. | -| touch | google.protobuf.BoolValue | Should we touch the ground and come back rather than stepping to a new place? | -| touch_offset | bosdyn.api.Vec2Value | |

    -

    +

    -
    -

    SwayParams

    -

    Parameters specific to Sway move.

    +
    +

    KneelCircleParams

    +

    Parameters specific to the kneel_circles move.

    @@ -1416,46 +1527,41 @@

    SwayParamsgoogle.protobuf.DoubleValue -

    + + + - - - + + + - + - - - - - - + - - - + + + - + - + - + - + -
    How far to move up/down [m].locationbosdyn.api.Vec3ValueLocation in body frame of the circle center. A typical value for the location is (0.4, 0, -0.5).
    horizontalgoogle.protobuf.DoubleValueHow far to move left/right [m].beats_per_circlegoogle.protobuf.Int32ValueHow beats per circle. One or two are reasonable values.
    rollnumber_of_circles google.protobuf.DoubleValueHow much to roll [rad].
    pivotPivotWhat point on the robot's body should the swaying be centered at. For example, should the head move instead of the butt?How many circles to perform. Mutually exclusive with beats_per_circle.
    styleSwayParams.SwayStyleWhat style motion should we use?offsetgoogle.protobuf.DoubleValueHow far apart the feet are when circling [m].
    pronouncedradius google.protobuf.DoubleValueHow pronounced should the sway-style be? The value is on a scale from [0,1.0].Size of the circles [m].
    hold_zero_axesreverse google.protobuf.BoolValueShould the robot hold previous values for the vertical, horizontal, and roll axes if the value is left unspecified (value of zero).Which way to circle.

    +

    -
    -

    TurnParams

    -

    Parameters specific to turning.

    +
    +

    KneelLegMove2Params

    +

    Parameters specific to KneelLegMove2 move.

    @@ -1466,50 +1572,51 @@

    TurnParamsgoogle.protobuf.DoubleValue -

    + - - - + + + - + - + - + - + - -
    How far to turn, described in radians with a positive value representing a turn to the left.Joint angles of the front left leg in radians.
    absolutegoogle.protobuf.BoolValueShould we turn to a yaw in choreography sequence frame?left_hip_ygoogle.protobuf.DoubleValue
    swing_heightleft_knee google.protobuf.DoubleValueSwing parameters to describe the footstep pattern during the turning [height in meters]. Note, a zero (or nearly zero) value will be considered as an unspecified parameter.
    swing_velocityright_hip_x google.protobuf.DoubleValueSwing parameter to describe the foot's swing velocity during the turning [m/s]. Note, a zero (or nearly zero) value will be considered as an unspecified parameter.Joint angles of the front right leg in radians.

    -
    -
    -

    TwerkParams

    -

    Parameters specific to twerking

    - - - - - + + + - - - + - + + + + + + + + + + + -
    FieldTypeDescriptionright_hip_ygoogle.protobuf.DoubleValue
    heightright_knee google.protobuf.DoubleValueHow far the robot should twerk in meters.
    easingEasingHow the motion should be paced.
    link_to_nextgoogle.protobuf.BoolValueShould we combine with the next move into a smooth trajectory.

    +

    -
    -

    WorkspaceArmMoveParams

    +
    +

    KneelLegMoveParams

    +

    Parameters specific to KneelLegMove move.

    @@ -1520,24 +1627,24 @@

    WorkspaceArmMoveParams

    - - - + + + - - - + + + - - - + + + - - - + + + @@ -1545,144 +1652,732 @@

    WorkspaceArmMoveParamsHow the motion should be paced.

    -
    rotationEulerZYXValueThe robot will rotate its body to the specified orientation (roll/pitch/yaw) [rad].hip_xgoogle.protobuf.DoubleValueJoint angles of the left front leg in radians. If mirrored, the joints will be flipped for the other leg.
    translationbosdyn.api.Vec3ValueThe positional offset to the robot's current location [m].hip_ygoogle.protobuf.DoubleValue
    absolutegoogle.protobuf.BoolValueGo to an absolute position/orientation? Otherwise, relative to starting pose.kneegoogle.protobuf.DoubleValue
    frameArmMoveFrameWhat frame is the motion specified in.mirrorgoogle.protobuf.BoolValueIf mirrored is true, the joints will be flipped for the leg on the other side (right vs left) of the body.
    easing

    +

    -
    -

    ArmMoveFrame

    +
    +

    Pace2StepParams

    +

    Parameters specific to pace translation.

    - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - + + - - - + + + + + + + + + + + + + + + + + + -
    NameNumberFieldType Description
    ARM_MOVE_FRAME_UNKNOWN0motionbosdyn.api.Vec2ValueHow far to move relative to starting position. [m]
    ARM_MOVE_FRAME_CENTER_OF_FOOTPRINT1absolute_motionbosdyn.api.Vec2ValueWhere to move relative to position at start of script. [m]
    ARM_MOVE_FRAME_HAND2motion_is_absolutegoogle.protobuf.BoolValueIs motion specified relative to pose at start of dance?
    ARM_MOVE_FRAME_BODY3swing_heightgoogle.protobuf.DoubleValueSwing parameters to describe the footstep pattern during the pace translation gait. Note, a zero (or nearly zero) value will be considered as an unspecified parameter.
    ARM_MOVE_FRAME_SHOULDER4swing_velocitygoogle.protobuf.DoubleValue
    ARM_MOVE_FRAME_SHADOW5yawgoogle.protobuf.DoubleValueHow far to turn, described in radians with a positive value representing a turn to the left.
    absolute_yawgoogle.protobuf.DoubleValueOrientation to turn to, relative to the orientation at the start of the script. [rad]
    yaw_is_absolutegoogle.protobuf.BoolValueShould we turn to a yaw in choreography sequence frame?
    absolutegoogle.protobuf.BoolValueDeprecation Warning *** DEPRECATED as of 3.0.0: The absolute field has been deprecated and split into the yaw_is_absolute and translation_is_absolute fields. The following field will be deprecated and moved to 'reserved' in a future release.

    +

    -
    -

    Easing

    -

    Enum to describe the type of easing to perform for the slices at either (or both) the -beginning and end of a move.

    +
    +

    RandomRotateParams

    +

    Parameters specific to the RandomRotate move.

    - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + + +
    NameNumberFieldType Description
    EASING_UNKNOWN0amplitudeEulerZYXValueThe amplitude [rad] of the rotation in each axis.
    EASING_LINEAR1speedEulerRateZYXValueThe speed [rad/s] of the motion in each axis.
    EASING_QUADRATIC_INPUT2speed_variationgoogle.protobuf.DoubleValueThe amount of variation allowed in the speed of the random rotations [m/s]. Note, this must be a positive value.
    EASING_QUADRATIC_OUTPUT3num_speed_tiersgoogle.protobuf.Int32ValueThe specified speed values will be split into this many number of tiers between the bounds of [speed - speed_variation, speed + speed variation]. Then a tier (with a specified speed) will be randomly choosen and performed for each axis.
    EASING_QUADRATIC_IN_OUT4tier_variationgoogle.protobuf.DoubleValueHow much can the output speed vary from the choosen tiered speed.

    +
    +
    +

    RippleColorParams

    + + - - - + + + + + - - + + - - + + - - + + - - + + - - + + -
    EASING_CUBIC_INPUT5FieldTypeDescription
    EASING_CUBIC_OUTPUT6mainColor
    EASING_CUBIC_IN_OUT7secondaryColor
    EASING_EXPONENTIAL_INPUT8patternRippleColorParams.Pattern
    EASING_EXPONENTIAL_OUTPUT9light_sideRippleColorParams.LightSide
    EASING_EXPONENTIAL_IN_OUT10increment_slicesgoogle.protobuf.DoubleValue

    +

    -
    -

    JumpParams.Lead

    -

    If split_fraction is non-zero, which legs to lift first.

    +
    +

    RotateBodyParams

    +

    Parameters for the robot rotating the body.

    - - + + - - - - - - - - + + + + + + + + + + +
    NameNumberFieldType Description
    LEAD_UNKNOWN0
    LEAD_AUTO1rotationEulerZYXValueThe robot will rotate its body to the specified orientation (roll/pitch/yaw).
    return_to_start_posegoogle.protobuf.BoolValueIf true, the robot will transition back to the initial pose we started at before this choreography sequence move begin execution, and otherwise it will remain in whatever pose it is in after completing the choreography sequence move.

    +
    +
    +

    RunningManParams

    +

    Parameters specific to RunningMan move.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    velocitybosdyn.api.Vec2Value
    swing_heightgoogle.protobuf.DoubleValueHow high to pick up the forward-moving feet [m].
    spreadgoogle.protobuf.DoubleValueHow far to spread the contralateral pair of feet [m].
    reversegoogle.protobuf.BoolValueShould we reverse the motion?
    pre_move_cyclesgoogle.protobuf.Int32ValueHow many full running man cycles should the robot complete in place before starting to move with the desired velocity.
    speed_multipliergoogle.protobuf.DoubleValueDo the move at some multiple of the dance cadence.
    duty_cyclegoogle.protobuf.DoubleValueWhat fraction of the time to have feet on the ground.
    com_heightgoogle.protobuf.DoubleValueHow high to hold the center of mass above the ground on average.

    +
    +
    +

    SetColorParams

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    left_colorColor
    right_same_as_leftgoogle.protobuf.BoolValue
    right_colorColor
    fade_in_slicesgoogle.protobuf.DoubleValue
    fade_out_slicesgoogle.protobuf.DoubleValue

    +
    +
    +

    SideParams

    +

    Parameters for moves that can go to either side.

    + + + + + + + + + + + + + + + +
    FieldTypeDescription
    sideSideParams.Side

    +
    +
    +

    StepParams

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    footLegWhich foot to use (FL = 1, FR = 2, HL = 3, HR = 4).
    offsetbosdyn.api.Vec2ValueOffset of the foot from it's nominal position, in meters.
    second_footLegShould we use a second foot? (None = 0, FL = 1, FR = 2, HL = 3, HR = 4).
    swing_waypointbosdyn.api.Vec3ValueWhere should the swing foot go? This vector should be described in a gravity-aligned body frame relative to the centerpoint of the swing. If set to {0,0,0}, uses the default swing path.
    swing_heightgoogle.protobuf.DoubleValueParameters for altering swing. Note that these will have no effect if swing_waypoint is specified. As well, a zero (or nearly zero) value will be considered as an unspecified parameter.

    meters | +| liftoff_velocity | google.protobuf.DoubleValue | m/s | +| touchdown_velocity | google.protobuf.DoubleValue | m/s | +| mirror_x | google.protobuf.BoolValue | Should we mirror the offset for the second foot? Ignored if second_foot is set to None | +| mirror_y | google.protobuf.BoolValue | | +| mirror | google.protobuf.BoolValue | Deprecation Warning *** DEPRECATED as of 2.3.0: The mirror field has been deprecated in favor for a more descriptive break down to mirror_x and mirror_y. The following field will be deprecated and moved to ‘reserved’ in a future release. | +| waypoint_dwell | google.protobuf.DoubleValue | What fraction of the swing should be spent near the waypoint. | +| touch | google.protobuf.BoolValue | Should we touch the ground and come back rather than stepping to a new place? | +| touch_offset | bosdyn.api.Vec2Value | |

    +

    +
    +
    +

    SwayParams

    +

    Parameters specific to Sway move.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    verticalgoogle.protobuf.DoubleValueHow far to move up/down [m].
    horizontalgoogle.protobuf.DoubleValueHow far to move left/right [m].
    rollgoogle.protobuf.DoubleValueHow much to roll [rad].
    pivotPivotWhat point on the robot's body should the swaying be centered at. For example, should the head move instead of the butt?
    styleSwayParams.SwayStyleWhat style motion should we use?
    pronouncedgoogle.protobuf.DoubleValueHow pronounced should the sway-style be? The value is on a scale from [0,1.0].
    hold_zero_axesgoogle.protobuf.BoolValueShould the robot hold previous values for the vertical, horizontal, and roll axes if the value is left unspecified (value of zero).

    +
    +
    +

    TurnParams

    +

    Parameters specific to turning.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    yawgoogle.protobuf.DoubleValueHow far to turn, described in radians with a positive value representing a turn to the left.
    absolute_yawgoogle.protobuf.DoubleValueOrientation to turn to, relative to the orientation at the start of the script. [rad]
    yaw_is_absolutegoogle.protobuf.BoolValueShould we turn to a yaw in choreography sequence frame?
    swing_heightgoogle.protobuf.DoubleValueSwing parameters to describe the footstep pattern during the turning [height in meters]. Note, a zero (or nearly zero) value will be considered as an unspecified parameter.
    swing_velocitygoogle.protobuf.DoubleValueSwing parameter to describe the foot's swing velocity during the turning [m/s]. Note, a zero (or nearly zero) value will be considered as an unspecified parameter.
    motionbosdyn.api.Vec2ValueHow far to move relative to starting position. [m]
    absolute_motionbosdyn.api.Vec2ValueWhere to move relative to position at start of script. [m]
    motion_is_absolutegoogle.protobuf.BoolValueIs motion specified relative to pose at start of dance?
    absolutegoogle.protobuf.BoolValueDeprecation Warning *** DEPRECATED as of 3.0.0: The absolute field has been deprecated and split into the yaw_is_absolute and translation_is_absolute fields. The following field will be deprecated and moved to 'reserved' in a future release.

    +
    +
    +

    TwerkParams

    +

    Parameters specific to twerking

    + + + + + + + + + + + + + + + +
    FieldTypeDescription
    heightgoogle.protobuf.DoubleValueHow far the robot should twerk in meters.

    +
    +
    +

    WorkspaceArmMoveParams

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    rotationEulerZYXValueThe robot will rotate its body to the specified orientation (roll/pitch/yaw) [rad].
    translationbosdyn.api.Vec3ValueThe positional offset to the robot's current location [m].
    absolutegoogle.protobuf.BoolValueGo to an absolute position/orientation? Otherwise, relative to starting pose.
    frameArmMoveFrameWhat frame is the motion specified in.
    easingEasingHow the motion should be paced.
    dance_frame_idgoogle.protobuf.Int32ValueIf we're using the dance frame, which one?

    +
    +
    +

    ArmMoveFrame

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameNumberDescription
    ARM_MOVE_FRAME_UNKNOWN0
    ARM_MOVE_FRAME_CENTER_OF_FOOTPRINT1
    ARM_MOVE_FRAME_HAND2
    ARM_MOVE_FRAME_BODY3
    ARM_MOVE_FRAME_SHOULDER4
    ARM_MOVE_FRAME_SHADOW5
    ARM_MOVE_FRAME_DANCE6

    +
    +
    +

    Easing

    +

    Enum to describe the type of easing to perform for the slices at either (or both) the +beginning and end of a move.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameNumberDescription
    EASING_UNKNOWN0
    EASING_LINEAR1
    EASING_QUADRATIC_INPUT2
    EASING_QUADRATIC_OUTPUT3
    EASING_QUADRATIC_IN_OUT4
    EASING_CUBIC_INPUT5
    EASING_CUBIC_OUTPUT6
    EASING_CUBIC_IN_OUT7
    EASING_EXPONENTIAL_INPUT8
    EASING_EXPONENTIAL_OUTPUT9
    EASING_EXPONENTIAL_IN_OUT10

    +
    +
    +

    FidgetStandParams.FidgetPreset

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameNumberDescription
    PRESET_UNKNOWN0
    PRESET_CUSTOM1
    PRESET_INTEREST2
    PRESET_PLAYFUL3
    PRESET_FEAR4
    PRESET_NERVOUS5
    PRESET_EXHAUSTED6

    +
    +
    +

    FrameSnapshotParams.Inclusion

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameNumberDescription
    INCLUSION_UNKNOWN0
    INCLUSION_IF_STANCE1
    INCLUSION_INCLUDED2
    INCLUSION_EXCLUDED3

    +
    +
    +

    JumpParams.Lead

    +

    If split_fraction is non-zero, which legs to lift first.

    + + + + + + + + + + + + + + + + + + @@ -1690,26 +2385,272 @@

    JumpParams.Lead

    + +
    +

    LedLight

    +

    NameNumberDescription
    LEAD_UNKNOWN0
    LEAD_AUTO1
    LEAD_FRONT
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameNumberDescription
    LED_LIGHT_UNKNOWN0
    LED_LIGHT_LEFT11
    LED_LIGHT_LEFT22
    LED_LIGHT_LEFT33
    LED_LIGHT_LEFT44
    LED_LIGHT_RIGHT15
    LED_LIGHT_RIGHT26
    LED_LIGHT_RIGHT37
    LED_LIGHT_RIGHT48

    +
    +
    +

    Leg

    +

    Enum to describe which leg is being referenced in specific choreography sequence moves.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameNumberDescription
    LEG_UNKNOWN0
    LEG_FRONT_LEFT1
    LEG_FRONT_RIGHT2
    LEG_HIND_LEFT3
    LEG_HIND_RIGHT4
    LEG_NO_LEG-1

    +
    +
    +

    Pivot

    +

    Enum for the pivot point for certain choreography sequence moves.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameNumberDescription
    PIVOT_UNKNOWN0
    PIVOT_FRONT1
    PIVOT_HIND2
    PIVOT_CENTER3

    +
    +
    +

    RippleColorParams.LightSide

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameNumberDescription
    LIGHT_SIDE_UNKNOWN0
    LIGHT_SIDE_LEFT1
    LIGHT_SIDE_RIGHT2
    LIGHT_SIDE_BOTH_IN_SEQUENCE3
    LIGHT_SIDE_BOTH_MATCHING4

    +
    +
    +

    RippleColorParams.Pattern

    + + + + + + + + + + + + + + + + + + + + + + + + + + - + + +
    NameNumberDescription
    PATTERN_UNKNOWN0
    PATTERN_FLASHING1
    PATTERN_SNAKE2
    PATTERN_ALTERNATE_COLORS 3
    LEAD_LEFTPATTERN_FINE_GRAINED_ALTERNATE_COLORS 4

    +
    +
    +

    SideParams.Side

    + + - - + + + + + + + + + + + + + + + + + + + -
    LEAD_RIGHT5NameNumberDescription
    SIDE_UNKNOWN0
    SIDE_LEFT1
    SIDE_RIGHT2

    +

    -
    -

    Leg

    -

    Enum to describe which leg is being referenced in specific choreography sequence moves.

    +
    +

    SwayParams.SwayStyle

    +

    The type of motion used by the Sway sequence move.

    @@ -1720,156 +2661,433 @@

    Leg

    - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    LEG_UNKNOWN0SWAY_STYLE_UNKNOWN0
    SWAY_STYLE_STANDARD1
    SWAY_STYLE_FAST_OUT2
    SWAY_STYLE_FAST_RETURN3
    SWAY_STYLE_SQUARE4
    SWAY_STYLE_SPIKE5
    SWAY_STYLE_PLATEAU6

    +
    +
    +
    +

    spot/choreography_sequence.proto

    +

    +
    +

    AnimateArm

    + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    joint_anglesArmJointAnglesFull arm joint angle specification.
    hand_poseAnimateArm.HandPoseThe hand position in the animation frame

    +
    +
    +

    AnimateArm.HandPose

    +

    An SE3 Pose for the hand where orientation is specified using either a quaternion or +euler angles

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    positionbosdyn.api.Vec3Value
    euler_anglesEulerZYXValueThe hand's orientation described with euler angles (yaw, pitch, roll).
    quaternionbosdyn.api.QuaternionThe hand's orientation described with a quaternion.

    +
    +
    +

    AnimateBody

    +

    The AnimateBody keyframe describes the body’s position and orientation. At least +one dimension of the body must be specified.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    body_posbosdyn.api.Vec3ValueThe body position in the animation frame.
    com_posbosdyn.api.Vec3ValueThe body's center of mass position in the animation frame.
    euler_anglesEulerZYXValueThe body's orientation described with euler angles (yaw, pitch, roll).
    quaternionbosdyn.api.QuaternionThe body's orientation described with a quaternion.

    +
    +
    +

    AnimateGripper

    + + + + + + + + + + + + + + + +
    FieldTypeDescription
    gripper_anglegoogle.protobuf.DoubleValue

    +
    +
    +

    AnimateLegs

    +

    The AnimateLegs keyframe describes each leg using either joint angles or the foot position.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    flAnimateSingleLegFront left leg.
    frAnimateSingleLegFront right leg.
    hlAnimateSingleLegHind left leg.
    hrAnimateSingleLegHind right leg.

    +
    +
    +

    AnimateSingleLeg

    +

    A single leg keyframe to describe the leg motion.

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    joint_anglesLegJointAnglesFull leg joint angle specification.
    foot_posbosdyn.api.Vec3ValueThe foot position of the leg in the animation frame.
    stancegoogle.protobuf.BoolValueIf true, the foot is in contact with the ground and standing. If false, the foot is in swing. If unset, the contact will be inferred from the leg joint angles or foot position.

    +
    +
    +

    Animation

    +

    Represents an animated dance move that can be used whithin choreographies after uploading.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - - + + + - - - + + + - - - + + + - -
    FieldTypeDescription
    namestringThe name of the animated move, which is how it will be referenced in choreographies.
    animation_keyframesAnimationKeyframeThe animated move is composed of animation keyframes, which specify the duration of each frame. The keyframe describes the position of the body/arms/gripper.
    controls_armboolIndicators as to which parts of the robot that the move controls.
    controls_legsbool
    controls_bodybool
    controls_gripperbool
    track_swing_trajectoriesboolTrack animated swing trajectories. Otherwise, takes standard swings between animated liftoff and touchdown locations.
    assume_zero_roll_and_pitchboolFor moves that control the legs, but not the body. If legs are specified by joint angles, we still need body roll and pitch to know the foot height. If assume_zero_roll_and_pitch is true, they needn't be explicitly specified.
    arm_playbackAnimation.ArmPlayback
    bpmdoubleOptional bpm that the animation is successful at.
    retime_to_integer_slicesboolWhen true, rescales the time of each keyframe slightly such that the move takes an integer number of slices. If false/absent, the move will be padded or truncated slightly to fit an integer number of slices.
    minimum_parametersAnimateParamsThe different parameters (minimum, default, and maximum) that can change the move. The min/max bounds are used by Choreographer to constrain the parameter widget, and will also be used when uploading a ChoreographySequence containing the animation to validate that the animated move is allowed.
    LEG_FRONT_LEFT1default_parametersAnimateParams
    LEG_FRONT_RIGHT2maximum_parametersAnimateParams
    LEG_HIND_LEFT3truncatableboolIndicates if the animated moves can be shortened (the animated move will be cut off). Not supported for leg moves.
    LEG_HIND_RIGHT4extendableboolIndicates if the animated moves can be stretched (animated move will loop). Not supported for leg moves.
    LEG_NO_LEG-1neutral_startboolIndicates if the move should start in a neutral stand position.

    -
    -
    -

    Pivot

    -

    Enum for the pivot point for certain choreography sequence moves.

    - - - - - + + + - - - - - + + + - - - + + + - - - + + + - - - + + + -
    NameNumberDescriptionprecise_stepsboolStep exactly at the animated locations, even at the expense of balance. By default, the optimizer may adjust step locations slightly.
    PIVOT_UNKNOWN0precise_timingboolTime everything exactly as animated, even at the expense of balance. By default, the optimizer may adjust timing slightly.
    PIVOT_FRONT1arm_requiredboolIf set true, this animation will not run unless the robot has an arm.
    PIVOT_HIND2arm_prohibitedboolIf set true, this animation will not run unless the robot has no arm.
    PIVOT_CENTER3no_loopingboolIf the animation completes before the move's duration, freeze rather than looping.

    +

    -
    -

    SideParams.Side

    +
    +

    AnimationKeyframe

    - - + + - - + + + + + + + + + + + + - - + + - - + + -
    NameNumberFieldType Description
    SIDE_UNKNOWN0timedoubleTime from the start of the animation for this frame.
    gripperAnimateGripperDifferent body parts the animated move can control. It can control multiple body parts at once.
    armAnimateArm
    SIDE_LEFT1bodyAnimateBody
    SIDE_RIGHT2legsAnimateLegs

    +

    -
    -

    SwayParams.SwayStyle

    -

    The type of motion used by the Sway sequence move.

    +
    +

    ArmJointAngles

    +

    The AnimateArm keyframe describes the joint angles of the arm joints in radians. +Any joint not specified, will hold the previous angle it was at when the keyframe +begins. At least one arm joint must be specified.

    - - + + - - - - - - - + + - - + + - - + + - - + + - - + + - - + + -
    NameNumberFieldType Description
    SWAY_STYLE_UNKNOWN0
    SWAY_STYLE_STANDARD1shoulder_0google.protobuf.DoubleValue
    SWAY_STYLE_FAST_OUT2shoulder_1google.protobuf.DoubleValue
    SWAY_STYLE_FAST_RETURN3elbow_0google.protobuf.DoubleValue
    SWAY_STYLE_SQUARE4elbow_1google.protobuf.DoubleValue
    SWAY_STYLE_SPIKE5wrist_0google.protobuf.DoubleValue
    SWAY_STYLE_PLATEAU6wrist_1google.protobuf.DoubleValue

    +

    -
    -
    -

    spot/choreography_sequence.proto

    -

    ChoreographerDisplayInfo

    Information for the Choreographer to display.

    @@ -1973,6 +3191,11 @@

    ChoreographerSavedouble UI specific member that describes exactly when the music should start, in slices. This is for time sync issues. + +choreography_start_slice +double +The start slice for the choreographer save. +

    @@ -2004,10 +3227,181 @@

    ChoreographySequenceAll of the moves in this choreography sequence. -

    +

    +

    +
    +

    ChoreographyStateLog

    + + + + + + + + + + + + + + + +
    FieldTypeDescription
    key_framesLoggedStateKeyFrameA set of key frames recorded at a high rate. The key frames can be for the duration of an executing choreography or for the duration of a manual recorded log (triggered by the StartRecordingState and StopRecordingState RPCs). The specific set of keyframes is specified by the LogType when requesting to download the data.

    +
    +
    +

    DownloadRobotStateLogRequest

    + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    headerbosdyn.api.RequestHeaderCommon request header
    log_typeDownloadRobotStateLogRequest.LogTypeWhich data should we download.

    +
    +
    +

    DownloadRobotStateLogResponse

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    headerbosdyn.api.ResponseHeaderCommon response header
    statusDownloadRobotStateLogResponse.StatusReturn status for the request.
    chunkbosdyn.api.DataChunkChunk of data to download. Responses are sent in sequence until the data chunk is complete. After receiving all chunks, concatenate them into a single byte string. Then, deserialize the byte string into an ChoreographyStateLog object.

    +
    +
    +

    ExecuteChoreographyRequest

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    headerbosdyn.api.RequestHeaderCommon request header
    choreography_sequence_namestringThe string name of the ChoreographySequence to use.
    start_timegoogle.protobuf.TimestampThe absolute time to start the choreography at. This should be in the robot's clock so we can synchronize music playing and the robot's choreography.
    choreography_starting_slicedoubleThe slice (betas/sub-beats) that the choreography should begin excution at.
    leasebosdyn.api.LeaseThe Lease to show ownership of the robot body.

    +
    +
    +

    ExecuteChoreographyResponse

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    headerbosdyn.api.ResponseHeaderCommon response header
    lease_use_resultbosdyn.api.LeaseUseResult
    statusExecuteChoreographyResponse.Status

    +
    +
    +

    LegJointAngles

    +

    Descprition of each leg joint angle (hip x/y and knee) in radians.

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    hip_xdouble
    hip_ydouble
    kneedouble

    -
    -

    ExecuteChoreographyRequest

    +
    +

    ListAllMovesRequest

    +

    Request a list of all possible moves and the associated parameters (min/max values).

    @@ -2022,31 +3416,41 @@

    ExecuteChoreographyRequestbosdyn.api.RequestHeader

    + +
    Common request header

    +
    +
    +

    ListAllMovesResponse

    +

    Response for ListAllMoves that defines the list of available moves and their parameter types.

    + + - - - + + + + + - - - + + + - - - + + + - - - + + + -
    choreography_sequence_namestringThe string name of the ChoreographySequence to use.FieldTypeDescription
    start_timegoogle.protobuf.TimestampThe absolute time to start the choreography at. This should be in the robot's clock so we can synchronize music playing and the robot's choreography.headerbosdyn.api.ResponseHeaderCommon response header
    choreography_starting_slicedoubleThe slice (betas/sub-beats) that the choreography should begin excution at.movesMoveInfoList of moves that the robot knows about.
    leasebosdyn.api.LeaseThe Lease to show ownership of the robot body.move_param_configstringA copy of the MoveParamsConfig.txt that the robot is using.

    +

    -
    -

    ExecuteChoreographyResponse

    +
    +

    LoggedFootContacts

    @@ -2057,26 +3461,30 @@

    ExecuteChoreographyResponsebosdyn.api.ResponseHeader -

    + + + - - + + - - + + + + + + + -
    Common response headerfr_contactboolBoolean indicating whether or not the robot's foot is in contact with the ground.
    lease_use_resultbosdyn.api.LeaseUseResultfl_contactbool
    statusExecuteChoreographyResponse.Statushr_contactbool
    hl_contactbool

    +

    -
    -

    ListAllMovesRequest

    -

    Request a list of all possible moves and the associated parameters (min/max values).

    +
    +

    LoggedJoints

    @@ -2087,16 +3495,40 @@

    ListAllMovesRequestbosdyn.api.RequestHeader -

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + -
    Common request headerflLegJointAnglesfront left leg joint angles.
    frLegJointAnglesfront right leg joint angles.
    hlLegJointAngleshind left leg joint angles.
    hrLegJointAngleshind right leg joint angles.
    armArmJointAnglesFull set of joint angles for the arm and gripper.
    gripper_anglegoogle.protobuf.DoubleValue

    +

    -
    -

    ListAllMovesResponse

    -

    Response for ListAllMoves that defines the list of available moves and their parameter types.

    +
    +

    LoggedStateKeyFrame

    @@ -2107,19 +3539,24 @@

    ListAllMovesResponse

    - - - + + + - - - + + + - - - + + + + + + + +
    headerbosdyn.api.ResponseHeaderCommon response headerjoint_anglesLoggedJointsFull set of joint angles for the robot.
    movesMoveInfoList of moves that the robot knows aboutfoot_contact_stateLoggedFootContactsFoot contacts for the robot.
    move_param_configstringA copy of the MoveParamsConfig.txt that the robot is using.animation_tform_bodybosdyn.api.SE3PoseThe current pose of the robot body in animation frame. The animation frame is defined based on the robot's footprint when the log first started recording.
    timestampgoogle.protobuf.TimestampThe timestamp (in robot time) for the key frame.

    @@ -2144,32 +3581,32 @@

    MoveInfoint32 -The number of "slices" (beats or sub-beats) that this move takes up. +The duration of this move in slices (usually 1/4 beats). -min_move_length_slices -int32 -The minimum number of "slices" that this move can complete in. +move_length_time +double +The duration of this move in seconds. If specified, overrides move_length_slices. is_extendable bool -If true, the user can extend the move beyond the requested length. +If true, the duration may be adjusted from the default specified by move_length_slices or move_length_time. -entrance_states -MoveInfo.TransitionState -The admissible states the robot can be in currently for this move to execute. +min_move_length_slices +int32 +Bounds on the duration may be adjusted in slices (usually 1/4 beats). These apply to extendable moves, but may also override move_length_time for some BPM. -exit_state -MoveInfo.TransitionState -The state of the robot after the move is complete. +max_move_length_slices +int32 + min_time double -The absolute minimum and maximum times of the move in seconds. +Bounds on the duration in time. These apply to extendable moves, but may also override move_length_slices for some BPM. max_time @@ -2177,6 +3614,16 @@

    MoveInfoMoveInfo.TransitionState +The admissible states the robot can be in currently for this move to execute. + + +exit_state +MoveInfo.TransitionState +The state of the robot after the move is complete. + + controls_arm bool Indicators as to which parts of the robot that the move controls. @@ -2197,10 +3644,25 @@

    MoveInfobool + + + +controls_annotations +bool + + + display ChoreographerDisplayInfo Information for the GUI tool to visualize the sequence move info. + +animated_move_generated_id +google.protobuf.StringValue +Unique ID for the animated moves. This is sent with the UploadAnimatedMove request and use to track which version of the animated move is currently saved on robot. The ID can be unset, meaning the RPC which uploaded the animation did not provide an identifying hash. +

    @@ -2262,99 +3724,293 @@

    MoveParamsTwerkParams - +twerk_params +TwerkParams + + + +chicken_head_params +ChickenHeadParams + + + +clap_params +ClapParams + + + +front_up_params +FrontUpParams + + + +sway_params +SwayParams + + + +body_hold_params +BodyHoldParams + + + +arm_move_params +ArmMoveParams + + + +kneel_leg_move_params +KneelLegMoveParams + + + +running_man_params +RunningManParams + + + +kneel_circle_params +KneelCircleParams + + + +gripper_params +GripperParams + + + +hop_params +HopParams + + + +random_rotate_params +RandomRotateParams + + + +crawl_params +CrawlParams + + + +side_params +SideParams + + + +bourree_params +BourreeParams + + + +workspace_arm_move_params +WorkspaceArmMoveParams + + + +figure8_params +Figure8Params + + + +kneel_leg_move2_params +KneelLegMove2Params + + + +fidget_stand_params +FidgetStandParams + + + +goto_params +GotoParams + + + +frame_snapshot_params +FrameSnapshotParams + + + +set_color_params +SetColorParams + + + +ripple_color_params +RippleColorParams + + + +fade_color_params +FadeColorParams + + + +independent_color_params +IndependentColorParams + + + +animate_params +AnimateParams + + + +

    +

    +
    +

    StartRecordingStateRequest

    + + + + + + + + + + + + + - - - + + + - - - + + + + +
    FieldTypeDescription
    headerbosdyn.api.RequestHeaderCommon request header
    chicken_head_paramsChickenHeadParamscontinue_recording_durationgoogle.protobuf.DurationHow long should the robot record for if no stop RPC is sent. A recording session can be extended by setting the recording_session_id below to a non-zero value matching the ID for the current recording session. For both start and continuation commands, the service will stop recording at end_time = (system time when the Start/Continue RPC is received) + (continue_recording_duration), unless another continuation request updates this end time. The robot has an internal maximum recording time of 5 minutes for the complete session log.
    clap_paramsClapParamsrecording_session_iduint64Provide the unique identifier of the recording session to extend the recording end time for. If the recording_session_id is 0, then it will create a new session and the robot will clear the recorded robot state buffer and restart recording. If this is a continuation of an existing recording session, than the robot will continue to record until the specified end time.

    +
    +
    +

    StartRecordingStateResponse

    + + - - - + + + + + - - - + + + - - + + - - - + + + + +
    front_up_paramsFrontUpParamsFieldTypeDescription
    sway_paramsSwayParamsheaderbosdyn.api.ResponseHeaderCommon response header
    body_hold_paramsBodyHoldParamsstatusStartRecordingStateResponse.Status
    arm_move_paramsArmMoveParamsrecording_session_iduint64Unique identifier for the current recording session

    +
    +
    +

    StopRecordingStateRequest

    + + - - - + + + + + - - - + + + + +
    kneel_leg_move_paramsKneelLegMoveParamsFieldTypeDescription
    running_man_paramsRunningManParamsheaderbosdyn.api.RequestHeaderCommon request header

    +
    +
    +

    StopRecordingStateResponse

    + + - - - + + + + + - - - + + + + +
    kneel_circle_paramsKneelCircleParamsFieldTypeDescription
    gripper_paramsGripperParamsheaderbosdyn.api.ResponseHeaderCommon response header

    +
    +
    +

    UploadAnimatedMoveRequest

    + + - - - + + + + + - - - + + + - - - + + + - - - + + + + +
    hop_paramsHopParamsFieldTypeDescription
    random_rotate_paramsRandomRotateParamsheaderbosdyn.api.RequestHeaderCommon request header
    crawl_paramsCrawlParamsanimated_move_generated_idgoogle.protobuf.StringValueUnique ID for the animated moves. This will be automatically generated by the client and is used to uniquely identify the entire animation by creating a hash from the Animation protobuf message after serialization. The ID will be conveyed within the MoveInfo protobuf message in the ListAllMoves RPC. This ID allows the choreography client to only reupload animations that have changed or do not exist on robot already.
    side_paramsSideParamsanimated_moveAnimationAnimatedMove to upload to the robot and create a dance move from.

    +
    +
    +

    UploadAnimatedMoveResponse

    + + - - - + + + + + - - - + + + - - + + - - - + + +
    bourree_paramsBourreeParamsFieldTypeDescription
    workspace_arm_move_paramsWorkspaceArmMoveParamsheaderbosdyn.api.ResponseHeaderCommon response header.
    figure8_paramsFigure8ParamsstatusUploadAnimatedMoveResponse.Status
    kneel_leg_move2_paramsKneelLegMove2ParamswarningsstringIf the uploaded animated move is invalid (will throw a STATUS_ANIMATION_VALIDATION_FAILED), then warning messages describing the failure cases will be populated here to indicate which parts of the animated move failed. Note: there could be some warning messages even when an animation is marked as ok.

    @@ -2410,7 +4066,42 @@

    UploadChoreographyResponse

    +

    +

    +
    +

    Animation.ArmPlayback

    +

    Mode for hand trajectory playback

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameNumberDescription
    ARM_PLAYBACK_DEFAULT0Playback as specified. Arm animations specified with joint angles playback in jointspace and arm animations specified as hand poses playback in workspace.
    ARM_PLAYBACK_JOINTSPACE1Playback in jointspace. Arm animation will be most consistent relative to the body
    ARM_PLAYBACK_WORKSPACE2Playback in workspace. Hand pose animation will be most consistent relative to the current footprint. Reference frame is animation frame.
    ARM_PLAYBACK_WORKSPACE_DANCE_FRAME3Playback in workspace with poses relative to the dance frame. hand pose animation will be most consistent relative to a fixed point in the world.

    ChoreographerDisplayInfo.Category

    @@ -2465,6 +4156,84 @@

    ChoreographerDisplayInfo.Category

    +

    +
    +

    DownloadRobotStateLogRequest.LogType

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameNumberDescription
    LOG_TYPE_UNKNOWN0Unknown. Do not use.
    LOG_TYPE_MANUAL1The robot state information recorded from the time of the manual start RPC (StartRecordingState) to either {the time of the manual stop RPC (StopRecordingState), the time of the download logs RPC, or the time of the internal service's buffer filling up}.
    LOG_TYPE_LAST_CHOREOGRAPHY2The robot will automatically record robot state information for the entire duration of an executing choreography in addition to any manual logging. This log type will download this information for the last completed choreography.

    +
    +
    +

    DownloadRobotStateLogResponse.Status

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameNumberDescription
    STATUS_UNKNOWN0Status unknown. Do not use.
    STATUS_OK1The log data downloaded successfully and is complete.
    STATUS_NO_RECORDED_INFORMATION2Error where there is no robot state information logged in the choreography service.
    STATUS_INCOMPLETE_DATA3Error where the complete duration of the recorded session caused the service's recording buffer to fill up. When full, the robot will stop recording but preserve whatever was recorded until that point. The robot has an internal maximum recording time of 5 minutes. The data streamed in this response will go from the start time until the time the buffer was filled.

    @@ -2545,6 +4314,70 @@

    MoveInfo.TransitionState

    +

    +
    +

    StartRecordingStateResponse.Status

    +

    The status for the start recording request.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameNumberDescription
    STATUS_UNKNOWN0Status unknown; do not use.
    STATUS_OK1The request succeeded and choreography has either started, or continued with an extended duration based on if a session_id was provided.
    STATUS_UNKNOWN_RECORDING_SESSION_ID2The provided recording_session_id is unknown: it must either be 0 (start a new recording log) or it can match the current recording session id returned by the most recent start recording request.
    STATUS_RECORDING_BUFFER_FULL3The Choreography Service's internal buffer is filled. It will record for a maximum of 5 minutes. It will stop recording, but save the recorded data until

    +
    +
    +

    UploadAnimatedMoveResponse.Status

    + + + + + + + + + + + + + + + + + + + + + + + + +
    NameNumberDescription
    STATUS_UNKNOWN0Do not use.
    STATUS_OK1Uploading + parsing the animated move succeeded.
    STATUS_ANIMATION_VALIDATION_FAILED2The animated move is considered invalid, see the warnings.

    @@ -2576,11 +4409,35 @@

    ChoreographyServiceUploadAnimatedMoveRequest +UploadAnimatedMoveResponse +Upload an animation to the robot. + + ExecuteChoreography ExecuteChoreographyRequest ExecuteChoreographyResponse Execute the uploaded dance. + +StartRecordingState +StartRecordingStateRequest +StartRecordingStateResponse +Manually start (or continue) recording the robot state. + + +StopRecordingState +StopRecordingStateRequest +StopRecordingStateResponse +Manually stop recording the robot state. + + +DownloadRobotStateLog +DownloadRobotStateLogRequest +DownloadRobotStateLogResponse stream +Download log of the latest recorded robot state information. +

    diff --git a/docs/html/docs/concepts/README.html b/docs/html/docs/concepts/README.html index 360067580..0b63020aa 100644 --- a/docs/html/docs/concepts/README.html +++ b/docs/html/docs/concepts/README.html @@ -8,7 +8,7 @@ - Concepts — Spot 2.3.5 documentation + Concepts — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -575,6 +616,7 @@

    ContentsGeometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • diff --git a/docs/html/docs/concepts/about_spot.html b/docs/html/docs/concepts/about_spot.html index 5c7348f53..44496acc4 100644 --- a/docs/html/docs/concepts/about_spot.html +++ b/docs/html/docs/concepts/about_spot.html @@ -8,7 +8,7 @@ - About Spot — Spot 2.3.5 documentation + About Spot — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/arm/README.html b/docs/html/docs/concepts/arm/README.html index 067d8ccf8..858120b5d 100644 --- a/docs/html/docs/concepts/arm/README.html +++ b/docs/html/docs/concepts/arm/README.html @@ -8,7 +8,7 @@ - Spot Arm and Gripper — Spot 2.3.5 documentation + Spot Arm and Gripper — Spot 3.0.0 documentation @@ -42,7 +42,7 @@ - + @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/arm/arm_concepts.html b/docs/html/docs/concepts/arm/arm_concepts.html index f5362603d..8668c0b80 100644 --- a/docs/html/docs/concepts/arm/arm_concepts.html +++ b/docs/html/docs/concepts/arm/arm_concepts.html @@ -8,7 +8,7 @@ - Arm Concepts — Spot 2.3.5 documentation + Arm Concepts — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/arm/arm_services.html b/docs/html/docs/concepts/arm/arm_services.html index c13dd9699..64ca812b5 100644 --- a/docs/html/docs/concepts/arm/arm_services.html +++ b/docs/html/docs/concepts/arm/arm_services.html @@ -8,7 +8,7 @@ - Arm Services — Spot 2.3.5 documentation + Arm Services — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -593,10 +634,11 @@

    ArmSurfaceContact Service

    Door Service

    -

    The door service is a framework for opening doors. We provide two command types:

    +

    The door service is a framework for opening doors. We support three command types:

      -
    • Auto This message requires users to specify a location to search for a door handle along with and some door parameters. Spot autonomously grabs the handle, opens the door, and walks through.

    • +
    • AutoGrasp: This message requires users to specify a location to search for a door handle along with and some door parameters. Spot autonomously grabs the handle, opens the door, and walks through.

    • Warmstart: In Warmstart, the assumption is the robot is already grasping the door handle. The robot will skip the grasp stage of auto, and immediately begin opening the door and then traverses through the doorway.

    • +
    • AutoPush: Used for doors that can be opened via a push without requiring a grasp. This includes pushbars, crashbars, and doors without a latching mechanism. The robot will point the hand down and push the door open with its wrist based on a push point supplied over the API.

    See the following example for using this service:

    Door Opening: diff --git a/docs/html/docs/concepts/arm/arm_specification.html b/docs/html/docs/concepts/arm/arm_specification.html index 1d4f952ef..4f65626e7 100644 --- a/docs/html/docs/concepts/arm/arm_specification.html +++ b/docs/html/docs/concepts/arm/arm_specification.html @@ -8,7 +8,7 @@ - Arm and gripper specifications — Spot 2.3.5 documentation + Arm and gripper specifications — Spot 3.0.0 documentation @@ -69,7 +69,7 @@

    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/autonomy/README.html b/docs/html/docs/concepts/autonomy/README.html index 72c42215e..5ac9d1af7 100644 --- a/docs/html/docs/concepts/autonomy/README.html +++ b/docs/html/docs/concepts/autonomy/README.html @@ -8,7 +8,7 @@ - Autonomy — Spot 2.3.5 documentation + Autonomy — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -571,13 +612,18 @@

    ContentsTypical autonomous navigation use case
  • Autonomous navigation services
  • GraphNav service
  • -
  • GraphNav map structure
  • +
  • GraphNav Map Structure
  • +
  • Recording and Modifying Maps
  • +
  • Map Processing
  • +
  • Anchorings and Anchoring Optimization
  • +
  • Map Data Transfer
  • Initialization
  • Localization
  • GraphNav and robot locomotion
  • Missions service
  • Network compute bridge
  • AutoReturn service
  • +
  • Directed Exploration
  • diff --git a/docs/html/docs/concepts/autonomy/auto_return.html b/docs/html/docs/concepts/autonomy/auto_return.html index 7ac7f2c06..6ba1975f6 100644 --- a/docs/html/docs/concepts/autonomy/auto_return.html +++ b/docs/html/docs/concepts/autonomy/auto_return.html @@ -8,7 +8,7 @@ - AutoReturn Service — Spot 2.3.5 documentation + AutoReturn Service — Spot 3.0.0 documentation @@ -41,7 +41,7 @@ - + @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/autonomy/autonomous_navigation_code_examples.html b/docs/html/docs/concepts/autonomy/autonomous_navigation_code_examples.html index a0d06716c..6c142e2d7 100644 --- a/docs/html/docs/concepts/autonomy/autonomous_navigation_code_examples.html +++ b/docs/html/docs/concepts/autonomy/autonomous_navigation_code_examples.html @@ -8,7 +8,7 @@ - Autonomous navigation code examples — Spot 2.3.5 documentation + Autonomous navigation code examples — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/autonomy/autonomous_navigation_services.html b/docs/html/docs/concepts/autonomy/autonomous_navigation_services.html index c7120fd0e..3bca282ea 100644 --- a/docs/html/docs/concepts/autonomy/autonomous_navigation_services.html +++ b/docs/html/docs/concepts/autonomy/autonomous_navigation_services.html @@ -8,7 +8,7 @@ - Navigation Services — Spot 2.3.5 documentation + Navigation Services — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/autonomy/components_of_autonomous_navigation.html b/docs/html/docs/concepts/autonomy/components_of_autonomous_navigation.html index 5ea1ea437..890fc1c6d 100644 --- a/docs/html/docs/concepts/autonomy/components_of_autonomous_navigation.html +++ b/docs/html/docs/concepts/autonomy/components_of_autonomous_navigation.html @@ -8,7 +8,7 @@ - Components of Navigation — Spot 2.3.5 documentation + Components of Navigation — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/autonomy/directed_exploration.html b/docs/html/docs/concepts/autonomy/directed_exploration.html new file mode 100644 index 000000000..89a547d3b --- /dev/null +++ b/docs/html/docs/concepts/autonomy/directed_exploration.html @@ -0,0 +1,680 @@ + + + + + + + + + + + Directed Exploration — Spot 3.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + + + +
    + +
    + + + + + + + + + + + + + + + + + +
    + +
      + +
    • »
    • + +
    • Concepts »
    • + +
    • Autonomy »
    • + +
    • Directed Exploration
    • + + +
    • + + + +
    • + +
    + + +
    +
    +
    +
    + +
    +

    Directed Exploration

    +
    +

    What is Directed Exploration?

    +

    Directed Exploration is a feature that enables Spot to navigate robustly when the environment has changed. When Spot’s path is blocked by an unexpected obstacle, Directed Exploration allows Spot to use its sensors to find a clear path to a nearby waypoint, even if that waypoint is not connected to Spot’s current waypoint in the GraphNav map. This enables Spot to deal with environments where new obstacles may have been added, old obstacles may have been moved, and doors may have been opened or closed.

    +

    For example, suppose you record the Autowalk mission shown below, where Spot visits the waypoints 1 through 6 in order. When you record the mission, door A is open and door B is closed.

    +

    Initial recorded path

    +

    Now, imagine someone closes door A and opens door B. Without Directed Exploration, Spot would get stuck at closed door A when it tried to replay the mission.

    +

    Changed environment

    +

    Directed Exploration will see that Spot can travel from waypoint 2 to waypoint 5 through open door B, even though this door was closed when the mission was recorded.

    +

    Directed Exploration path

    +

    After reaching waypoint 5, Spot will navigate to the final destination at waypoint 6.

    +
    +
    +

    When is Directed Exploration invoked?

    +

    Directed Exploration is invoked as a last resort when all other attempts to find a path to the destination have failed. Other strategies include attempting to plan a different route through the edges in the GraphNav map and attempting to follow alternate waypoints that are created next to the waypoints on the map.

    +

    Directed Exploration can succeed in some situations where these other strategies fail, because Directed Exploration frees Spot from the constaint of navigating along edges stored in the GraphNav map. Directed Exploration allows Spot to use its sensors to determine whether it can reach nearby waypoints, even if those waypoints aren’t connected to its current location in the GraphNav map.

    +
    +
    +

    How to enable/disable Directed Exploration

    +

    Directed Exploration is enabled by default.

    +

    To disable Directed Exploration when replaying an Autowalk mission from the tablet, uncheck the box marked “Drive around large obstacles” on the mission upload screen. This will disable both Directed Exploration and alternate waypoints, but it will still allow replanning alternate routes through the GraphNav map.

    +

    To disable Directed Exploration for a NavigateTo or NavigateRoute command issued through the SDK, set the disable_directed_exploration field of the corresponding TravelParams proto to true.

    +

    To disable Directed Exploration for a mission requested through the SDK, set the disable_directed_exploration field of the PlaySettings proto in the PlayMissionRequest to true.

    +
    +
    + + +
    + +
    + +
    +
    + +
    + +
    + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/html/docs/concepts/autonomy/docking.html b/docs/html/docs/concepts/autonomy/docking.html index a4584de44..93f0f8f2a 100644 --- a/docs/html/docs/concepts/autonomy/docking.html +++ b/docs/html/docs/concepts/autonomy/docking.html @@ -8,7 +8,7 @@ - Docking — Spot 2.3.5 documentation + Docking — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/autonomy/graphnav_and_robot_locomotion.html b/docs/html/docs/concepts/autonomy/graphnav_and_robot_locomotion.html index 4d8eba4f1..1ba39be6e 100644 --- a/docs/html/docs/concepts/autonomy/graphnav_and_robot_locomotion.html +++ b/docs/html/docs/concepts/autonomy/graphnav_and_robot_locomotion.html @@ -8,7 +8,7 @@ - GraphNav and Robot Locomotion — Spot 2.3.5 documentation + GraphNav and Robot Locomotion — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/autonomy/graphnav_map_structure.html b/docs/html/docs/concepts/autonomy/graphnav_map_structure.html index ddc44b46e..b38cf3984 100644 --- a/docs/html/docs/concepts/autonomy/graphnav_map_structure.html +++ b/docs/html/docs/concepts/autonomy/graphnav_map_structure.html @@ -8,7 +8,7 @@ - GraphNav Map Structure — Spot 2.3.5 documentation + GraphNav Map Structure — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -571,11 +612,120 @@

    View Map Python ExampleThe viewer also shows fiducials that were detected during the map recording process. Fiducials are shown as blue squares labeled with the fiducial ID. If multiple fiducials with the same ID are displayed near each other, this represents multiple detections taken at different times during recording.

    Maps do not have a global coordinate system (like GPS coordinates, for example). Only the relative transformations between waypoints are known.

    + +
    +

    Recording and Modifying Maps

    +

    The GraphNav map recording service is used to create and modify maps using robot data. Using the StartRecording RPC, you can tell the map recording service to begin creating a new map. You may then drive the robot around a site, and it will record a map. Afterwards (or at intervals during recording), you may download map data.

    +

    The recording_command_line example in the Spot SDK shows how to record and modify maps at runtime, and how to download map data from the recording service.

    Creating waypoints

    Waypoints are created by the robot every two meters during map recording. However, they can be created in areas where the robot’s path has more curvature as it navigates around corners and obstacles.

    Call the CreateWaypoint RPC at specific locations in order to perform some action in a mission or when you want to explicitly maintain some mapping between a user event and a waypoint ID. Example: creating a waypoint at an inspection point.

    +
    +

    Creating edges

    +

    Edges are created automatically while recording is active. One edge will be created between every new waypoint that the recording service creates.

    +

    If you want to create edges manually, you can call the CreateEdge RPC in the recording service.

    +
    +
    +
    +

    Map Processing

    +

    Starting in the 3.0 SDK release, the GraphNav map processing service makes two new options available to modify maps, Topology Processing (automatic loop closure) and Anchoring Optimization. The map processing service is an RPC service interface which shares a map with the GraphNav Service. The map processing Service performs operations on the GraphNav map, and can either return results to the user, or write results into the shared map. The map processing service may be used after recording a map using the Map Recording Service.

    +

    The recording_command_line example has examples for how to use the map processing service on a newly recorded GraphNav map.

    +
    +

    Topology Processing

    +

    The Topology of a GraphNav map refers to the connectivity between different Waypoints using Edges. Below are some examples of Maps with different topologies.

    +

    Topologies

    +

    The recording service records one kind of map topology: the Chain. Chains always go from a starting point to an ending point. Chains are easy to create – it only requires recording the robot’s current position and sensor data relative to the previous pose and sensor data. There are no connections between waypoints that aren’t along the chain, even if they happen to cross each other in physical space.

    +

    When starting a new recording while the robot is localized to an existing waypoint, the recording service will create a Branch – which is simply a Chain starting from a waypoint within another Chain.

    +

    Loops are topologies where there is more than one possible path between two waypoints. The recording service is unable to create loops on its own. To create loops, you can manually call the CreateEdge RPC, or, starting in SDK 3.0, you may use the map processing service to automatically close loops.

    +
    +

    Why are loops important?

    +

    Any time the robot crosses its own path in physical space, there is an opportunity to create a topological connection (an edge) between where the robot currently is, and where it was in the past. This is called “loop closure”.

    +

    The robot has returned to the start

    +

    For example, if the robot has returned to its starting place after traversing hundreds of meters through a site, it would be desirable to know that the start and end points of the map are topologically connected.

    +

    For example, consider the chain:

    +
    start waypoint -> (1,000 waypoints) -> end waypoint
    +
    +
    +

    The robot may only travel along edges while using GraphNav, so without an edge between the starting and ending waypoints of the map, the robot is unable to travel betwen the start and end directly. For example, the command:

    +
    NavigateTo(start waypoint)
    +
    +
    +

    causes the robot to traverse the 1,000 waypoints it had recorded in reverse rather than walking directly from the end to the start.

    +

    To prevent this, we must close the loop between the starting and ending waypoint.

    +
    +
    +
    +

    Automatic Loop Closure

    +

    You can issue a ProcessTopologyRequest to the map processing service to automatically close loops in the GraphNav map. This helps the robot know which areas of the map are topologically connected so that it can make smarter decisions about initializing its localization to the map and planning around it.

    +

    Note that Loop Closures in the Autonomy SDK affect both localization and navigation – an edge is expected to be traversable by the robot in a physical sense (that is, the robot can walk from point A to point B if there is an edge between A and B), and it also encodes the relative transformation between the waypoints it connects.

    +

    Topology processing example

    +

    There are two types of loop closure currently supported: odometry loop closures and fiducial loop closures.

    +

    Odometry loop closures occur whenever:

    +
      +
    • The robot walks a total path length of less than 50 meters (for base platform robots) or 100 meters (for robots with LIDAR) and believes it is back at the same location (within 4 meters).

    • +
    • That path does not cross a staircase.

    • +
    • The resulting loop closure edge does not cross any body obstacles.

    • +
    +

    To close larger loops, the map processing service relies on fiducials. Fiducial loop closures occur whenever:

    +
      +
    • The robot sees the same fiducial twice.

    • +
    • In both instances that the robot sees the fiducial, the fiducial was less than 4 meters away and had low measurement error.

    • +
    • The resulting loop closure edge does not cross any body obstacles.

    • +
    +
    +

    Note: the Autowalk app automatically calls topology processing after recording ends. SDK users must make sure to manually call the map processing service to enable this feature. See the recording_command_line.py example for how to call this service.

    +
    +
    +

    Ensuring good topology

    +

    To make sure that the map processing service has the best chance of closing loops, adhere to the following guidelines:

    +
      +
    • The more fiducials in the environment, the better.

    • +
    • If the map is multi-floor, there must be at least one fiducial per floor.

    • +
    • Try to pass as closely as possible to fiducials while recording.

    • +
    • Try to record multiple pathways through the same site to maximize the chance that the robot will be able to navigate around obstacles.

    • +
    +
    +
    +
    +
    +

    Anchorings and Anchoring Optimization

    +

    The 3.0 SDK introduces a new concept for GraphNav maps called anchorings. In addition to waypoints and edges, GraphNav maps now have an attached anchoring for waypoints and fiducials.

    +

    An anchoring is a mapping from waypoints to some global reference frame. That is, for every waypoint and fiducial, we have an SE3Pose describing the transform from a seed frame to that waypoint or fiducial.

    +

    For example, to get all of the positions of the waypoints relative to the seed frame, you can do this:

    +
    # For each waypoint in the graph's anchoring, prints the (x, y, z) position of that waypoint.
    +def print_anchorings(graph):
    +    for anchor in graph.anchoring.anchors:
    +        pos = anchor.seed_tform_waypoint.position
    +        print("id: {} x: {} y: {} z: {}".format(anchor.id, pos.x, pos.y, pos.z))
    +
    +
    +
    +

    Note: it is not necessary for every waypoint in the graph to have an anchoring. GraphNav will use edge transformations to interpolate between the provided anchors.

    +
    +

    All GraphNav maps have a default anchoring, which comes from the User Origin if it exists, or the starting waypoint otherwise. The default anchoring is computed by extrapolating robot odometry over the edges in the graph.

    +

    Additionally, the map processing tool can be used to optimize the anchoring via the ProcessAnchoringRequest RPC. An example for how to use this RPC using its default parameters can be found in the recording_command_line example. A more complex example can be found in the graph_nav_anchoring_optimization SDK example.

    +

    Anchoring optimization can be used to improve the metric consistency of a map’s anchoring. An example is shown below:

    +

    Anchoring before optimization +A real, 1km graph_nav map displayed from above before anchoring optimization is applied. The long red and yellow lines are loop closures from topology processing. The numbers are instances of fiducials stored in individual waypoints.

    +

    Anchoring after optimization +The same map after anchoring optimization has been applied. Notice that segments of the building which were disjoint before now align.

    +
    +

    Note: the Autowalk app automatically calls anchoring optimization after recording ends. SDK users must make sure to manually call the anchoring optimization RPC to access this feature.

    +
    +
    +

    What can you do with anchorings?

    +

    Anchorings can be used to describe the global relationships and layout between waypoints in a GraphNav map. The pose of the robot relative to the anchoring is also available in the Localization message returned by GraphNav as seed_tform_body.

    +

    Anchorings may also be used to display the GraphNav map relative to a blueprint, BIM model, or other pre-existing map. For details on how to achieve this, please see the graph_nav_anchoring_optimization SDK example.

    +

    There is also an SDK example for how to use GraphNav anchorings to extract a global 3D point cloud from a map and save it as a PLY file. This is located in the graph_nav_extract_point_cloud SDK example.

    +

    An extracted point cloud of a real GraphNav map An extracted point cloud from a real GraphNav map, viewed in a 3rdparty tool called CloudCompare.

    +

    Starting in SDK release 3.0, anchorings can also be used to command the robot. The NavigateToAnchor command can be used to send the robot to an approximate x, y, z position relative to the seed frame. To do this, the robot will navigate over a series of waypoints and edges from its current location to the nearest waypoint to the given commanded pose, and will then walk in a straight line toward the commanded pose.

    +
    +
    +
    +

    Map Data Transfer

    +

    The GraphNav service has one active map instance on the robot that it shares with the GraphNav recording service. You can download the data stored in this map instance, or upload new data to the robot to replace or extend the current map instance.

    Downloading maps

    Waypoints and edges have associated snapshots that store data the robot uses to compute localization and inform the robot’s locomotion.

    diff --git a/docs/html/docs/concepts/autonomy/graphnav_service.html b/docs/html/docs/concepts/autonomy/graphnav_service.html index 2948a3e60..1f8f7c01b 100644 --- a/docs/html/docs/concepts/autonomy/graphnav_service.html +++ b/docs/html/docs/concepts/autonomy/graphnav_service.html @@ -8,7 +8,7 @@ - GraphNav Service — Spot 2.3.5 documentation + GraphNav Service — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/autonomy/graphnav_tech_summary.html b/docs/html/docs/concepts/autonomy/graphnav_tech_summary.html index 70fddec2f..496b59fab 100644 --- a/docs/html/docs/concepts/autonomy/graphnav_tech_summary.html +++ b/docs/html/docs/concepts/autonomy/graphnav_tech_summary.html @@ -8,7 +8,7 @@ - Autonomy Technical Summary — Spot 2.3.5 documentation + Autonomy Technical Summary — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/autonomy/initialization.html b/docs/html/docs/concepts/autonomy/initialization.html index f74af6ea4..a6fa63c6f 100644 --- a/docs/html/docs/concepts/autonomy/initialization.html +++ b/docs/html/docs/concepts/autonomy/initialization.html @@ -8,7 +8,7 @@ - GraphNav Initialization — Spot 2.3.5 documentation + GraphNav Initialization — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -612,7 +653,7 @@

    Placing fiducials

    Initializing with search

    -

    If fiducials aren’t available, the client program can use other methods of initialization through the SetLocalization RPC (available in the graph_nav.proto). The client can provide an initial guess for the complete localization in the initial_guess field which describes the robot’s pose relative to a known waypoint and which waypoint to initialize to. Details on the algorithm that is run when a initial guess is provided are described in the next section.

    +

    If fiducials are not available, the client program can use other methods of initialization through the SetLocalization RPC (available in the graph_nav.proto). The client can provide an initial guess for the complete localization in the initial_guess field which describes the robot’s pose relative to a known waypoint and which waypoint to initialize to. Details on the algorithm that is run when a initial guess is provided are described in the next section.

    If the initial guess for the localization is unknown, then GraphNav can perform a brute force search. The parameters of that search are set in the SetLocalization RPC via max_distance and max_yaw fields. Depending on the size of these parameters, the SetLocalization RPC can take a long time to complete.

    diff --git a/docs/html/docs/concepts/autonomy/localization.html b/docs/html/docs/concepts/autonomy/localization.html index a8f7a77bb..85ba26c32 100644 --- a/docs/html/docs/concepts/autonomy/localization.html +++ b/docs/html/docs/concepts/autonomy/localization.html @@ -8,7 +8,7 @@ - GraphNav Localization — Spot 2.3.5 documentation + GraphNav Localization — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/autonomy/missions_service.html b/docs/html/docs/concepts/autonomy/missions_service.html index 7ac5423ef..f4e44bd34 100644 --- a/docs/html/docs/concepts/autonomy/missions_service.html +++ b/docs/html/docs/concepts/autonomy/missions_service.html @@ -8,7 +8,7 @@ - Mission Service — Spot 2.3.5 documentation + Mission Service — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/autonomy/typical_autonomous_navigation_use_case.html b/docs/html/docs/concepts/autonomy/typical_autonomous_navigation_use_case.html index fb865c60e..da29605b5 100644 --- a/docs/html/docs/concepts/autonomy/typical_autonomous_navigation_use_case.html +++ b/docs/html/docs/concepts/autonomy/typical_autonomous_navigation_use_case.html @@ -8,7 +8,7 @@ - Typical Autonomous Use Case — Spot 2.3.5 documentation + Typical Autonomous Use Case — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/base_services.html b/docs/html/docs/concepts/base_services.html index 527888d2a..f391c6a48 100644 --- a/docs/html/docs/concepts/base_services.html +++ b/docs/html/docs/concepts/base_services.html @@ -8,7 +8,7 @@ - Base Services — Spot 2.3.5 documentation + Base Services — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -637,6 +678,12 @@

    time-sync +

    lease

    +

    The lease service provides methods to achieve ownership over controlling the robot and maintaining valid communication with that owner.

    +

    The lease is required to issue commands that control the robots mobility and motion, such as powering on the robot or commanding stand. To start, the lease must be acquired (or taken) to show ownership of the robot resources. Then retain lease signals must be sent throughout the duration of the operation to preserve ownership and indicate reliable communication with the lease owner. Ultimately the lease should be returned to free the resources to be reclaimed; however, it can be revoked during operation by another user or if the communication signals are not received within a certain period of time.

    +

    The Lease documentation provides a more in-depth description of leases, typical lease usage, and how to understand lease errors.

    +

    diff --git a/docs/html/docs/concepts/bddf.html b/docs/html/docs/concepts/bddf.html index 9bd8bd9fe..d581b07af 100644 --- a/docs/html/docs/concepts/bddf.html +++ b/docs/html/docs/concepts/bddf.html @@ -8,7 +8,7 @@ - BDDF data format — Spot 2.3.5 documentation + BDDF data format — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/choreography/README.html b/docs/html/docs/concepts/choreography/README.html index 711a2b972..4dfd8992f 100644 --- a/docs/html/docs/concepts/choreography/README.html +++ b/docs/html/docs/concepts/choreography/README.html @@ -8,7 +8,7 @@ - Spot Choreography SDK — Spot 2.3.5 documentation + Spot Choreography SDK — Spot 3.0.0 documentation @@ -42,7 +42,7 @@ - + @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -570,9 +611,16 @@

    Contents diff --git a/docs/html/docs/concepts/choreography/animation_file_specification.html b/docs/html/docs/concepts/choreography/animation_file_specification.html new file mode 100644 index 000000000..998991dbf --- /dev/null +++ b/docs/html/docs/concepts/choreography/animation_file_specification.html @@ -0,0 +1,831 @@ + + + + + + + + + + + Animation Files for Choreographer — Spot 3.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + + + +
    + +
    + + + + + + + + + + + + + + + + + +
    + + + + +
    +
    +
    +
    + +
    +

    Animation Files for Choreographer

    +

    Choreographer supports animations that are parsed from a specific file format into an Animation protobuf message, which will be uploaded to the robot using the UploadAnimation RPC. Animation files are human readable/editable text files with a *.cha extension. The animation file format consists of three sections: the options section, the parameters section, and the key frames section.

    +

    There are three methods to parse an animation cha file into an Animation protobuf message:

    +
      +
    1. Create an animations folder in the same directory as the Choreographer executable. All *.cha animation files within this folder will be automatically parsed and uploaded to robot when Choreographer is opened. The directory structure will look like this:

    2. +
    +
    dance_directory/
    +	choreographer.exe
    +	animations/
    +		bourree_arm.cha
    +		my_animation.cha
    +
    +
    +
      +
    1. In Choreographer, click “File” -> “Load Animated Move” to upload a single animation file after the application is already opened.

    2. +
    3. The python script animation_file_to_proto.py (in the bosdyn-choreography-client package) will parse the text file and can be used to output a protobuf message.

    4. +
    +
    +
    +

    File Specification

    +
    +

    File Name and Extension

    +

    The animation text file will have the *.cha extension. The filename will become the move name, which can be referenced in choreography sequences. Choreographer will display the animated move name with underscores converted to spaces and each word capitalized.

    +
    +
    +

    Structure

    +

    The animation cha file consists of three sections. Each section must be present and separated by a blank line such that the animation file parser can succeed. In all three sections, values within the same line can be separated by any combination of spaces and tabs.

    +

    The file sections are:

    +
      +
    • Options: pre-defined information used by both Choreographer and the robot to interpret the animation correctly.

    • +
    • Parameters: adjustable values to customize the animated move further. These values appear in the Parameters section of Choreographer.

    • +
    • Body Keyframes: the complete set of key frames, either densely or sparsely specified, for a fixed amount of time. The move duration/keyframe timestamps are indicated by either a timestamp in this section, or computed from the frequency defined in the options section). The keyframes consist of either joint angles or poses for the body parts being controlled.

    • +
    +
    +
    +

    Units

    +

    All units are in:

    +
      +
    • Distance: meters

    • +
    • Angles: radians

    • +
    • Time: seconds. Sometimes time is measured in “slices” (¼ beat), so the duration is dependent on the sequence’s BPM (beats per minute).

    • +
    +
    +
    +

    Commenting Support

    +

    We support comments within the animation files. A comment is marked with either “#” or “//” and can take up an entire line in the file or be at the end of an existing line. Comments must be within one of the three main sections of the file, and they cannot create a new section.

    +
    +
    +
    +

    Options File Section

    +

    The options section defines how the animated move is controlled and interpreted by both Choreographer and the robot. The section consists of lines with a keyword at the beginning of the line, and a fixed number of values separated by spaces following the keyword. The number of values is specific to the keyword used and defined below. Some keywords, like “neutral_start”, have no values following the keyword.

    +

    The options section must contain a definition for which tracks the animation controls. This is specified by the “controls” keyword, and a line structured as follows:

    +
    controls TRACK1... TRACK_N
    +
    +
    +

    Following the keyword “controls”, the fields TRACK1 … TRACK_N are one or more of [legs, body, arm, gripper] (e.g. controls legs body for a move that controls the legs and body but not the arm or gripper.)

    +
    +

    Supported Keywords for the Options Section

    +

    Other than the “controls” tracks keyword, the other keywords in the options section are optional. The following are the supported key words and defining values for this section and what they define about the animated move:

    +

    bpm VALUE: Where VALUE is the nominal beats per minute of the animation. If the script is at a different BPM, the animation will be time-scaled so it takes the same number of beats. If not specified, the animation will play at the nominal speed regardless of script BPM, taking a variable number of beats.

    +

    extendable: Indicates the move can be stretched in Choreographer. Doing so will cause the animation to loop.

    +

    truncatable: Indicates that the move can be shortened in Choreographer. Doing so will cause the animation to end early.

    +

    display_rgb RED GREEN BLUE: Where RED, GREEN, BLUE are integer numbers between 0 and 255. This defines the color the move block will display as within Choreographer. If not present, a default color will be generated based on the hash of the file name.

    +

    frequency HZ: Where HZ is the number of frames per second in the animation body. If unspecified, the time column must be present in the body.

    +

    retime_to_integer_slices: Rescales time slightly so that the move takes an integer number of slices. If absent, the animation will be padded or truncated slightly to take an integer number of slices.

    +

    description TEXT: Where TEXT to be displayed within Choreographer as a description of the animation.

    +

    neutral_start: Applies only to moves that control the body but do not contain leg information. Indicates the move can be assumed to start in a neutral stand. If not specified, it is instead assumed that the center of footprint is at the origin of the animation frame.

    +

    precise_steps: Step exactly at the animated locations, even at the expense of balance.

    +

    precise_timing: Step exactly at the animated times, even at the expense of balance.

    +

    track_swing_trajectories: Track animated swing trajectories. Otherwise, takes standard swings between animated liftoff and touchdown locations.

    +

    arm_playback OPTION: Where OPTION is one of the following. If OPTION is not included the default behavior is for arm animations specified as joint angles to playback as joint space and animations specified as hand poses to default to workspace.

    +
      +
    • jointspace: Arm animation playback is with respect to the body.

    • +
    • workspace: Arm animation playback is with respect to the current footprint.

    • +
    • workspace_dance_frame: Arm animation playback is with respect to a dance frame in workspace. Parameter arm_dance_frame_id can be used to specify which dance frame if multiple exist.

    • +
    +

    requires_arm: If set true, can not be loaded on a robot without an arm.

    +

    no_looping: If the animation completes before the move’s duration, freeze rather than looping. Without this option, the animation might loop because either the move duration was extended or because the speed parameter was set to a value greater than 1.

    +
    +
    +
    +

    Parameters File Section

    +

    The parameters section defines adjustable values for the animated move. Each parameter value specified will show in the parameters panel in Choreographer when the animation is selected to be edited/added. The parameter names coincide with the values present in the AnimateParams protobuf message (in choreography_params.proto). The animation parameters may only apply for moves which control specific tracks (arm, gripper, body,legs). Each parameter is described on a single line and can be specified two different ways:

    +
      +
    1. The parameter name, followed by the minimum, default, and maximum value (in that order).

    2. +
    +
    PARAMETER_NAME    MINIMUM_VALUE    DEFAULT_VALUE    MAXIMUM_VALUE
    +
    +
    +
      +
    1. Only the parameter name, and the limits/default will be configured from predefined values within Choreographer.

    2. +
    +
    PARAMETER_NAME
    +
    +
    +

    Note: If no parameters are needed, put the keywords “no parameters” as the parameter section such that the file will still be parsed correctly.

    +
    +

    Supported parameters pertaining to all tracks

    +

    speed: Play the animation at this time multiplier.

    +

    offset_slices: Start the move with the script at this slice.

    +
    +
    +

    Supported parameters when controlling the body track

    +

    body_entry_slices: How many slices to spend transitioning smoothly from the previous pose to the animated pose trajectory for body motion.

    +

    body_exit_slices: How many slices to spend transitioning from the animated trajectory back to the nominal pose. If set to 0, will not transition back.

    +

    translation_multiplier.x: Multiply the body motion in the x direction.

    +

    translation_multiplier.y: Multiply the body motion in the y direction.

    +

    translation_multiplier.z: Multiply the body motion in the z direction.

    +

    rotation_multiplier.roll: Multiply the body motion in the roll direction.

    +

    rotation_multiplier.pitch: Multiply the body motion in the pitch direction.

    +

    rotation_multiplier.yaw: Multiply the body motion in the yaw direction.

    +

    body_tracking_stiffness: How hard to try to track the animated body motion. Only applicable to animations that control both the body and the legs. On a scale of 1 to 10 (11 for a bit extra). Higher will result in more closely tracking the animated body motion, but possibly at the expense of balance for more difficult animations.

    +
    +
    +

    Supported parameters when controlling the arm track

    +

    arm_entry_slices: How many slices to spend transitioning smoothly from the previous pose to the animated pose trajectory for arm and gripper motion.

    +

    shoulder_0_offset: Offset to add to the SH0 angle in all animation keyframes.

    +

    shoulder_1_offset: Offset to add to the SH1 angle in all animation keyframes.

    +

    elbow_0_offset: Offset to add to the EL0 angle in all animation keyframes.

    +

    elbow_1_offset: Offset to add to the EL1 angle in all animation keyframes.

    +

    wrist_0_offset: Offset to add to the WR0 angle in all animation keyframes.

    +

    wrist_1_offset: Offset to add to the WR1 angle in all animation keyframes.

    +

    arm_required: Prevents a robot without an arm from loading the animation.

    +

    arm_prohibited: Prevents a robot with an arm from loading the animation.

    +
    +
    +

    Supported parameters when controlling the gripper track

    +

    gripper_offset: Offset to add to the gripper angle in all animation keyframes.

    +

    gripper_multiplier: Multiply all gripper angles by this value.

    +

    gripper_strength_fraction: How hard the gripper can squeeze. Fraction of full strength.

    +
    +
    +

    Supported parameters when controlling either (or both) the arm and gripper tracks

    +

    arm_dance_frame_id: Dance frame to reference for workspace arm moves. Only valid in combination with the option arm_playback workspace_dance_frame (specified in the options section of the file).

    +
    +
    +
    +

    Body Keyframe File Section

    +

    The body keyframe section defines the actual animated move through keyframes describing either the pose or joint angles at each keyframe timestamp. Each keyframe is a single line consisting of a number of fields determined by the number of columns specified. The first line will specify what value is in each column and how many columns there will be; it is written as a series of key words (space separated) where each keyword has an fixed number of columns that the parser expects.

    +

    Columns defined by keywords can either be an individual definition, or a group definition describing a fixed number of values. For example, “body_pos” describes a group of three columns, and “body_x body_y body_z” describes those same three columns using the individual column keywords.

    +

    The same column value cannot be specified multiple times. Column values are only defined in the first line of the body keyframe section. Some columns are multiple ways of specifying the same body control (for example, “leg_joints” and “foot_pos”) and will be mutually exclusive when defining an animated move.

    +

    Column definitions are required for each track that is being controlled (as specified in the options section). If additional columns controlled unspecified tracks are included, they will be ignored when the animated move is executed.

    +
    +

    Supported Columns Not Pertaining to a Particular Track

    +

    time: The timestamp of each frame. The start of the animation is 0. This column and the “frequency” option are mutually exclusive, but one is required.

    +
    +
    +

    Supported Columns Pertaining to Gripper Track

    +

    gripper: Gripper joint angle. Required.

    +

    The field gripper is required.

    +
    +
    +

    Supported Columns Pertaining to Arm Track

    +

    arm_joints: Grouping of 6 columns [shoulder0 shoulder1 elbow0 elbow1 wrist0 wrist1]. Represents the arm joint angles. Any columns not included will be held constant at the previous joint angle. Mutually exclusive with hand_pos and hand orientation specifications.

    +

    hand_pos: Grouping of [hand_x hand_y hand_z]. Hand position in the animation frame.

    +

    hand_quat_wxyz: Grouping of [hand_quat_w hand_quat_x hand_quat_y hand_quat_z]. Mutually exclusive with other orientation specifications.

    +

    hand_quat_xyzw: Grouping of hand_quat_x hand_quat_y hand_quat_z hand_quat_w]. Mutually exclusive with other orientation specifications.

    +

    hand_euler_rpy: Grouping of [hand_roll hand_pitch hand_yaw] Mutually exclusive with other orientation specifications.

    +
    +
    +

    Supported Columns Pertaining to Body Track

    +

    body_pos: Grouping of [body_x body_y body_z]. Body position in the animation frame. Mutually exclusive with com_pos.

    +

    com_pos: Grouping of [com_x com_y com_z]. Center of Mass position in the animation frame. Mutually exclusive with body_pos.

    +

    body_quat_wxyz: Grouping of [body_quat_w body_quat_x body_quat_y body_quat_z]. Mutually exclusive with other orientation specifications.

    +

    body_quat_xyzw: Grouping of [body_quat_x body_quat_y body_quat_z body_quat_w]. Mutually exclusive with other orientation specifications.

    +

    body_euler_rpy: Grouping of [body_roll body_pitch body_yaw] Mutually exclusive with other orientation specifications.

    +

    At least one dimension of the body must be specified.

    +
    +
    +

    Supported Columns Pertaining to Legs Track

    +

    leg_joints: Grouping of [fl_hx fl_hy fl_kn fr_hx fr_hy fr_kn hl_hx hl_hy hl_kn hr_hx hr_hy hr_kn]. Can also be grouped by leg as [fl_angles fr_angles hl_angles hr_angles] Mutually exclusive (by leg) with foot_pos.

    +

    foot_pos: Grouping of [fl_x fl_y fl_z fr_x fr_y fr_z hl_x hl_y hl_z hr_x hr_y hr_z]. Can also be grouped by leg as [fl_pos fr_pos hl_pos hr_pos]. Mutually exclusive (by leg) with leg_joints.

    +

    contact: Grouping of [fl_contact fr_contact hl_contact hr_contact]. 1 if in stance. 0 if in swing. If absent, contact will be inferred from either leg_joints or foot_pos.

    +

    Either leg_joints or foot_pos are required for each leg.

    +
    +
    + + +
    + +
    + +
    +
    + +
    + +
    + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/html/docs/concepts/choreography/animations_in_choreographer.html b/docs/html/docs/concepts/choreography/animations_in_choreographer.html new file mode 100644 index 000000000..c652e4132 --- /dev/null +++ b/docs/html/docs/concepts/choreography/animations_in_choreographer.html @@ -0,0 +1,681 @@ + + + + + + + + + + + Animations in Choreography — Spot 3.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + + + +
    + +
    + + + + + + + + + + + + + + + + + +
    + + + + +
    +
    +
    +
    + +
    +

    Animations in Choreography

    +

    The Choreography service is a framework for producing scripted motion through a list of customizable, predetermined moves. The dances can be customized through the track layering system, the parameters associated with each move, and the adjustable beats per minute of the dances. While these knobs provide a large amount of flexibility when authoring choreographies, there are scenarios where the exact output can’t fully be expressed through the existing moves. To do this, we have developed an animation pipeline and API in the 2.4 Spot software release.

    +

    The animation pipeline allows you to create wholly custom sequences using 3D animation tools and integrate them in Choreographer scripts just like the predefined, default moves. For the intro sequence of the “Spot’s On It” video, we used Autodesk Maya to produce the kaleidoscoping dance moves. Autodesk Maya is a 3D animation software that gives fine-grain control for authoring and editing kinematics trajectories, but a range of tool sets can be used with this API.

    +

    The base representation for the animation is a human-readable text file, so while an animated dance move can be created through 3D animation tools like Autodesk Maya, it can also be hand-written and edited. The primary component of the animated move is a set of timestamped key frames which specify the motion of the different tracks (arms, gripper, legs, or body). The timestamped keyframes that specify the animation can be densely spaced, as they would be from a 3D animation software, or they can be very sparse and the robot will interpolate between each key frame.Additionally, there are components of this text file which enable different animation-specific options and also specifies the different parameters which are associated with this move. A complete overview of the animation file format can be found in the “Animation File Specification” document.

    +

    The animated move text files are be parsed into an Animation protobuf messages and uploaded to the robot. Once the animation is uploaded to the robot, it can be referenced by name within choreographies. This parsing step (from text file to protobuf message) happens automatically in Choreographer for both animations in the “animations” directory that matches the directory structure shown below, and also for animations uploaded through “File”->”Load Animated Move” menus.

    +
    dance_directory/
    +	choreographer.exe
    +	animations/
    +		bourree_arm.cha
    +		my_animation.cha
    +
    +
    +

    There is a python script in the bosdyn-choreography-client package, called animation_file_to_proto.py, which provides client access to the same functions used by Choreographer to complete the text file to protobuf animation parsing.

    +

    When Choreographer is opened, it begins uploading all animations automatically to every connected robot. Once an animation is uploaded, it will last on the robot until it is rebooted. A dialog will appear indicating the status of all animations being uploaded; the image below shows an example of this dialog. If an animation fails to upload, then check the terminal where the Choreographer executable is running. An error message should be present on the terminal and provide a description of the part of the animation that was invalid.

    +

    Animation Upload Dialog

    +
    +

    Choreography Logs for Animations

    +

    To aid in creating animated dance moves without 3D animation software, we have added choreography logs. These logs can be recorded through the choreography log service while driving the robot around with the tablet, moving the robots arm around manually (while it is powered off), or running an existing choreography. The choreography log contains high-rate timestamped key frames with the robot’s joint state, foot contacts, and body pose relative to the animation frame (defined by the robot’s foot state when the choreography log started). These key frames can be used directly for the animation file’s key frames.

    +

    The choreography logs are divided into two types: auto and manual. The log types are used to specify the log’s duration and when it is recorded. The “auto” log is recorded whenever a choreography is being executed by the robot. The robot will start recording an “auto” log automatically when a choreography begins, and stop 3 seconds after the completion of a choreography. The “manual” log can be recorded whenever and its duration is defined by when the robot receives a StartRecordingState RPC to when it receives a StopRecordingState RPC. The manual log has a maximum of 5 minutes of recording. The robot will only keep the most recent “auto” log and the most recent “manual” log saved - therefore the logs must be downloaded immediately to ensure data is not lost.

    +

    In Choreographer, there are buttons to start/stop recording the “manual” log (shown below). As well, there are buttons to download each log type (shown below). The log can be downloaded as a pickle file, which tightly packages the data stored as a python dictionary into a serialized object. This is the default format and will be used if no file extension is specified in the file name when saving the log. Additionally, it can be downloaded as a text file, which saves the ChoreographyStateLog protobuf message as a protobuf-to-text message; to save in this format, when typing the log name the “.txt” should be explicitly included.

    +

    Choreographer Log Buttons

    +

    The API for the choreography logs is described in the Choreography Service document and can be accessed via the API in the choreography client.

    +
    +
    + + +
    + +
    + +
    +
    + +
    + +
    + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/html/docs/concepts/choreography/choreographer.html b/docs/html/docs/concepts/choreography/choreographer.html index 0c5b5bf77..2cbdbe2bf 100644 --- a/docs/html/docs/concepts/choreography/choreographer.html +++ b/docs/html/docs/concepts/choreography/choreographer.html @@ -8,7 +8,7 @@ - Boston Dynamics Choreographer User Guide — Spot 2.3.5 documentation + Boston Dynamics Choreographer User Guide — Spot 3.0.0 documentation @@ -41,7 +41,7 @@ - + @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -562,48 +603,70 @@

    Boston Dynamics Choreographer User Guide

    Running Choreographer

    -

    Choreographer is an executable program we will provide you via a download link at the Support Center. To run the program, simply download it at the link we will provide you, and execute it. Note that the Choreographer executable is only available for Windows. If you wish to run Choreographer from Mac or Linux, we will need to provide you with Python wheels and installation instructions (advanced usage only). Please contact us if that is the case.

    +

    Choreographer is an executable program we will provide you via a download link at the Support Center. To run the program, simply download it at the link we will provide you, and execute it. Note that the Choreographer executable is only available for Windows and Linux.

    If you wish to run Choreographer connected to a Spot, please see the “Connecting Robots to Choreographer” section.

    Choreography Safety

    -

    When testing your choreography sequence on a robot, always keep in mind basic safety procedures . Make sure there is plenty of space around your Spot, keep all Spots at least two meters apart from each other, and be sure that neither you nor anyone else approach the dancing Spot. Never approach your Spot unless its motors have been powered off.

    +

    When testing your choreography sequence on a robot, always keep in mind basic safety procedures. Make sure there is plenty of space around your Spot, keep all Spots at least two meters apart from each other, and be sure that neither you nor anyone else approach the dancing Spot. Never approach your Spot unless its motors have been powered off.

    +
    +

    Beginner vs Advanced Mode

    +

    One of the goals for Choreographer is to provide a tool that gives the user as much freedom as they could possibly want. As such, you will be able to find combinations of moves, parameters, and BPM that Spot cannot reliably perform under all conditions.

    +

    Beginner mode provides a more controlled experience that will be more likely to yield reliable results. This mode has smaller parameter ranges which allows for less energetic but generally more reliable dances. Additionally, it has some of the more dynamic dance moves removed from the Moves List and does not provide any support for animated dance moves.

    +

    By default, in the 2.4 released Choreographer executable, it will automatically load in Beginner mode. You can enable it by using the --restricted argument when starting Choreographer from the command line. As well, if you are in Advanced mode and go to the Settings menu and select load in Beginner mode, it will take effect and load in this mode next time you open Choreographer.

    +

    You can switch to Advanced mode by selecting the load in Advanced mode checkbox in Choreographer’s welcome menu, or navigating to “Settings” and selecting load in Advanced mode (which will take effect next time you open Choreographer). Note that if you create a dance in Advanced mode, Choreographer may not be able to load it while in Beginner mode if the dance’s parameters are outside its reduced range.

    +

    It is highly recommended that Choreographer users start in Beginner mode until they are comfortable using the robot and creating/executing choreographies!

    +

    Interface Overview

    -

    Interface Guide

    -

    The Choreographer interface consists of seven main areas. They are:

    +

    +

    The Choreographer interface consists of the following important key sections/buttons:

      -
    1. Moves List - Here you can find all of our predefined moves, sorted by general category.

    2. +
    3. Moves List - Here you can find all of our predefined moves (both stock moves and animation moves), sorted by general category, such as “Body” or “Transition”.

    4. Dance Timeline - This is the main area of the Choreographer, and it shows a representation of your dance over time. Each move is a different block, which can be clicked to edit the parameters, dragged around, copy-pasted, or potentially stretched or shrunk if the move parameter’s allow it.

    5. -
    6. Robot Controls - The robot controls are a row of buttons you can use to send commands to any robots connected to Choreographer, including starting and stopping your dance, and powering on or off your robot’s motors. (Note, this row is disabled when you are not connected to any robots, as shown above.)

    7. +
    8. Dance Tabs - Multiple choreography sequences can be opened at once, and will appear as different tabs above the timeline.

    9. Move Name - When you select a move in the Moves List, its name and description will appear here.

    10. Robot Preview - This section gives you a preview of the robot’s body and arm during your selected move. Note that this section only appears for select moves that directly position the body of the robot or moves which control the arm.

    11. -
    12. Move Parameters - When you select a move that is customizable, its different, adjustable parameters will appear here. You can modify them to adjust how the robot will act during this move. Be sure to test to make sure the robot can handle your parameters! Sometimes more extreme parameters can be too much for the robot during high or low BPM songs, so if a combination of parameters don’t work, adjust them until they do for your situation! Near each parameter’s name, a blue question mark may appear which will provide a description of the specific parameter.

    13. +
    14. Move Parameters - When you select a move that is customizable, its different, adjustable parameters will appear here. You can modify them to adjust how the robot will act during this move. Be sure to test and make sure the robot can handle your parameters! Sometimes more extreme parameters can be too much for the robot during high or low BPM songs, so if a combination of parameters don’t work, adjust them until they do for your situation! Near each parameter’s name, a blue question mark may appear which will provide a description of the specific parameter if you hover the mouse over it.

    15. +
    16. Robot Controls - The robot controls are a row of buttons you can use to send commands to any robots connected to Choreographer, including starting and stopping your dance, and powering on or off your robot’s motors. (Note, this row is disabled when you are not connected to any robots, as shown above.)

    17. Music Controls - This row of controls lets you load and play a song to play during your robot’s dance, manually adjust the BPM of your robot’s moves to match that of your song or the volume of the music, and stop the music and the robot’s dancing if one is connected.

    18. +
    19. Move Configuration/Robot Management Tabs - Tabs to toggle between the move configuration tab (displays move name and parameters), and the robot management tab (displays all active robot connections and health statistics).

    20. +
    21. Mode Indicator - Choreographer has “Beginner” and “Advanced” modes. The title provides an indicator to help remember which mode the application is loaded in. There is an option in the “Settings” menu to switch modes when the application is next re-opened.

    22. +
    23. Add/Disconnect Buttons - Buttons which enable dynamically changing which robots are connected and controlled by Choreographer.

    24. +
    25. Robot Connections - Each row indicates the robot hostname and other information regarding the robot currently connected to Choreographer. The checkbox indicates whether or not the robot is being controlled (e.g. when unchecked, pressing robot control buttons like “sit” will do nothing to the unselected robot).

    26. +
    27. Dance Selector - Drop down menu to choose which of the open choreographies the robot will execute when both checked (in the “Selected” column) and start choreography is clicked.

    28. +
    29. Health Stats - Columns which show the power state (on/off), battery state of charge, and any faults for each robot.

    Choreography File Basics

    -

    Spot choreography files consist mainly of a sequence of predefined moves that can be arranged in the timeline of the choreographer (Interface #2). Each move can contain any combination of Gripper, Arm, Body, Legs, or multiple body parts that it affects, and will appear on the appropriate track(s) within the timeline. You can mix and match so that the legs can do a move such as Step while the body does a move such as Rotate Body. However, moves may not overlap on any tracks.

    +

    Spot choreography sequence files consist mainly of a sequence of predefined moves that can be arranged in the timeline of Choreographer. Each move can contain any combination of Gripper, Arm, Body, Legs, Lights or multiple body parts that it affects, and will appear on the appropriate track(s) within the timeline. You can mix and match so that the legs can do a move such as Step while the body does a move such as Rotate Body. However, moves may not overlap on any tracks.

    All choreography files are assumed to be 4/4 signatures in the Choreographer UI. The timeline is broken up into quarter notes (thick vertical lines), each of which is broken up by four lighter vertical lines. Each of those 16th-note intervals is known as a Slice. All moves must be a whole number of slices, and each move must begin and end at a slice boundary. How many slices a move takes is dependent on the BPM (Beats Per Minute) of your song. See Loading Music for more information on how to change your BPM.

    All choreography files are assumed to be 4/4 signatures in the Choreographer UI. The timeline is broken up into quarter notes (thick, vertical lines labeled with the note number). Each quarter note is further divided into four beats (light grey, vertical lines). Then, each 16th-note interval is known as a slice (dotted, vertical lines). Specifically, Choreographer considers there to be 4 slices per every beat, and 4 beats per every note. All moves must be a whole number of slices, and each move must begin and end at a slice boundary. The number of slices a move will take is dependent on the BPM (beats per minute) of your song. This can be adjusted in the “Music Controls” bar; see the Loading Music section for more information.

    For example, this “Running Man” move has been extended from the default number of slices, and will now control the legs track for the first 12 slices of this script:

    -

    Slice Diagram

    +

    Slice Diagram

    Adding Moves

    -

    To add a move to the timeline, there are multiple different methods. You can single click the move in the Moves List, which will open the parameters of the move, but not add it to the timeline. As well, the up and down arrow keys will navigate between different moves in the Moves List once one is selected. While you have a move selected in the Moves List, you can adjust its parameters in the Move Parameters section.

    -

    Once the parameters are adjusted to the desired values, the move can be added to the dance timeline by either 1) pressing the Add button beneath the moves list, 2) double clicking the move name in the Moves List, or 3) entering Insert Mode (described below). If you adjust a move’s parameters and then add it to the timeline, the new Move block that appears in the timeline will have those same modified parameters.

    -

    The icon , in the upper left of the Timeline view, will enter Insert Mode when pressed. When in Insert Mode, you can click anywhere in the Timeline to add the selected move (with any of the parameter modifications you have made) to the Timeline at that point. To then exit Insert Mode and re-enter the default Choreographer mode, press the button or hit the escape key on the keyboard.

    +

    There are multiple different methods to add moves to the timeline. You can single click the move in the Moves List, which will open the name, description, and parameters of the move in the Move Configuration Tab, but will not add it to the timeline. As well, the up and down arrow keys will navigate between different moves in the Moves List once one is selected. While you have a move selected in the Moves List, you can adjust its parameters in the Move Parameters section.

    +

    Once the parameters are adjusted to the desired values, the move can be added to the dance timeline by any of the following methods:

    +
      +
    1. Pressing the Add button beneath the moves list, which appends the move to the end of the timeline with any parameter changes.

    2. +
    3. Double clicking the move name in the Moves List, which appends the move to the end of the timeline with any parameter changes.

    4. +
    5. Click the toggle to go from “Append” to “Insert” (under the Moves List). In “insert” mode, hovering the mouse of the timeline will show a “ghost” move block, which when clicked will be added to the timeline with any parameter changes. To exit “insert” mode, hit the escape key on the keyboard or press the toggle again to return to “append” mode. +Insert/Append Mode Toggle

    6. +
    7. Drag a move from the Moves List into the timeline with any parameter changes. Like insert mode, a ghost move block will appear and the mouse can be moved to determine the location to drop and insert the move into the timeline.

    8. +

    Modifying Move Blocks

    -

    Once a move is added to your Timeline, it can be dragged left and right to the appropriate time. Some, but not all, of the moves can be resized by clicking and dragging on the edge of the move’s block. The move will automatically enforce any requirements it has about minimum or maximum duration. Note, to help you with longer moves, the Timeline can be zoomed in/out using the Zoom bar above it.

    +

    Once a move is added to your Timeline, it can be dragged left and right to the appropriate time. Some, but not all, of the moves can be resized by clicking and dragging on the edge of the move’s block; hover the mouse over the edge of the move and if it can be resized it will show an arrow instead of the regular mouse. The move will automatically enforce any requirements it has about minimum or maximum duration. Note, to help you with longer moves, the Timeline can be zoomed in/out using the Zoom bar below it.

    Modifying Move Parameters

    To modify a move’s parameters, simply click it on the timeline to select it, and modify the parameters that appear in the Move Parameters section. Each move has different parameters, and some may not have any parameters at all. Please see the Moves Reference Guide for descriptions of what each parameter does for each move type. Each numerical parameter can be modified by editing its text field, adjusting its slider, or pressing the Up or Down arrow buttons. Boolean parameters can be changed by checking or unchecking the box. Enum parameters are changed by choosing new values in the drop down menu.

    +

    A move’s parameters can be modified before it is added to the timeline as well. Once the move is selected in the Moves List, the parameters can be edited and when the move is ultimately added to the timeline, it will contain these parameter modifications.

    Robot Preview

    @@ -611,12 +674,13 @@

    Robot Preview

    Selecting Multiple Moves

    -

    To select multiple moves, click on empty space in the Timeline view, and then drag over the moves that should be selected. To unselect all the moves, click in empty space on the Time view. Note that you cannot edit the parameters of multiple moves at once, but you can drag them around the timeline or copy and paste them all.

    +

    To select multiple moves, click on empty space in the Timeline view, and then drag over the moves that should be selected. To unselect all the moves, click in empty space on the Time view. Note that you cannot edit the parameters of multiple moves at once, but you can drag them around the timeline, delete them all, or copy and paste them all.

    Copy / Pasting / Deleting Moves

    -

    When you have any number of moves selected, you can Copy (Ctrl+C or Edit->Copy) and Paste (Ctrl+V or Edit->Paste) them as you wish. When you paste moves into your choreography sequence, the new moves will attempt to appear as close as they can to the original move’s location, moving right on the timeline until they can find a place they fit. You can also right click them and choose “Clone” to instantly create a copy of your selected moves, which will also be inserted as close as they can fit into your choreography sequence.

    -

    To delete moves, simply select them and either press Delete or Backspace, or choose Edit->Delete in the menus.

    +

    When you have any number of moves selected, you can Copy (Ctrl+C, Edit->Copy) and Paste (Ctrl+V or Edit->Paste) them as you wish. When you paste moves into a choreography sequence, the new moves will attempt to appear as close as they can to the original move’s location, moving right on the timeline until they can find a place they fit. Moves can be copied and pasted between different open dance tabs.

    +

    You can also right click a single move or move grouping and choose “Clone” to instantly create a copy of your selected moves in the currently opened dance, which will also be inserted as close as they can fit into your choreography sequence.

    +

    To delete moves, simply select them and either press the Delete or Backspace keys, choose Edit->Delete in the top-left menus, or right click and select “Delete”.

    Loading Music

    @@ -624,129 +688,30 @@

    Loading Music -

    Red+Green Sliders

    -

    The red slider allows the user to start the dance at a different location than the beginning; the dance will start at the move associated with the closest slice to the slider’s location. The green slider allows the user to adjust when the music starts; the music will begin playing when the dance reaches the slice closest to the slider’s location. The picture below circles the two sliders; the lines drawn at the center of the sliders show exactly where in the timeline the slider’s location is. They can be moved by clicking on the colored boxes and dragging them to the desired location.

    -

    Sliders

    +
    +

    Red Slider

    +

    The red slider allows the user to start the dance at a different location than the beginning; the dance will start at the move associated with the closest slice to the slider’s location and the music will start at the timestamp within the song associated with the slider’s location. The line drawn at the center of the sliders show exactly where in the timeline the slider’s location is, as shown in the image below. It can be moved by clicking on the colored boxes and dragging it to the desired location.

    +

    Slider

    Previewing Moves

    If you have a robot connected to the Choreographer, you can preview moves before adding them to the Timeline. Simply select a move from the Moves List, modify its parameters however you want, and press the Preview Move button. This will cancel all current dances and actions on the robot, and it will perform the one move you have selected. This is a great way to test out parameter modifications before adding it to your move sequence.

    +

    Preview Move Button

    Performing Choreography Sequences

    -

    The “Start Choreography” button in the Robot Controls bar will upload the choreography from the sequence viewer to the robot, and then send a command to execute the routine to the robot while beginning the music at the same starting time specified in the choreography sent to the robot. There will be at least a three second delay (programmed into the button) to ensure that the music and routine can begin at the same time on the robot. Note, if the robot is not started in the proper position (sprawl, sit, stand) and has to automatically make transitions to be ready for the first move in the routine, then the timing of the music starting and the choreography sequence starting will likely be incorrect.

    -

    To stop the choreography routine or the music playing, the “Stop” button will return the robot to a standing position and stop the music. In an emergency, use E-Stop or Power Off instead.

    +

    The “Start Choreography” button in the Robot Controls bar will upload the choreography (whichever is selected in the drop down for the robot in the Robot Management Tab - by default, this is the current open choreography tab) to the robot, and then send a command to execute the routine to the robot while beginning the music at the same starting time specified in the choreography sent to the robot. There will be at least a three second delay (programmed into the button) to ensure that the music and routine can begin at the same time on the robot. To adjust this delay, the command line argument --delay DELAY_IN_SECONDS can be passed when starting the Choreographer application from the command line.

    +

    Note, if the robot is not started in the proper position (sprawl, sit, stand) and has to automatically make transitions to be ready for the first move in the routine, then the timing of the music starting and the choreography sequence starting will likely be incorrect.

    +

    To stop the choreography routine (or just stop the music from playing if no robot is connected/executing a choreography), the “Stop” button will freeze the robot with all four feet on the ground and stop the music. In an emergency, use “E-Stop” or “Power Off” buttons instead.

    Saving and Loading Choreography Files

    You can save and load Choreographer routines that you create. To save your current file, either press Ctrl+S or go to File->Save. Your routine will be saved in protobuf text format, which you can then open and easily read with your own scripts if you wish. To then load a file you’ve saved, press Ctrl+L or go to File->Load Choreography.

    Additionally, you can append an existing choreography sequence at the end of your current dance by going to File->Append Choreography (Ctrl+E), which will automatically add all of the move blocks from that file to the end of your current routine. This is particularly useful if you want to construct a choreography sequence from smaller premade sequences.

    -
    -

    Connecting Robots to Choreographer

    -

    Robots cannot currently be connected to or disconnected from Choreographer while it is running. In order to connect your robot to Choreographer, you must start Choreographer from the command line and pass in the arguments --hostname {IP/Hostname of your Spot} --user {Username you use to log in to your Spot} --password {Password for your Spot}. If you wish to connect to multiple Spots at once, simply add more copies of those command line arguments, one set for each Spot. Note that all Spots will do the exact routine, and the Choreographer program does not yet support individual routines for each Spot. In order to accomplish that, you can save your individual routine files and write a custom script to execute them both at the same time on each robot.

    -
    -
    -

    Robot Controls

    -

    The Robot Controls bar is disabled if there are no robots connected to the Choreographer program. If a robot is connected, the buttons will have the following effects:

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ButtonFunction
    Power OffPowers off the Spot’s motors. Always press this before approaching your Spot.
    Power OnPowers on your Spot’s motors. You must activate this before your Spot can stand or start choreography.
    E-StopEnables or disables your Spot’s E-Stop. In an emergency, use this to stop the Spot immediately.
    Self-RightIf your Spot has fallen, this will attempt to right it into a sitting position
    SitSits the Spot in-place. Cancels all current choreography and music, but E-Stop or Power Off should be used in an emergency.
    StandBrings Spot to a stand. Cancels all current choreography and music, but E-Stop or Power Off should be used in an emergency.
    Enable JoystickActivated Joystick Controls (see Joystick Controls section)
    Enable WASD DrivingActivates "WASD" keyboard driving (see WASD Controls section)
    Start ChoreographySends your choreography sequence to your Spot, then starts a 3 second countdown before the robot begins dancing. Any loaded music will automatically start as soon as Spot begins to dance.
    -
    -

    Joystick Controls

    -

    ../../../_images/image1.png

    -

    A X-Box gamepad controller can be used with the GUI for convenience of moving and positioning the robot. The button layout is set up for X-Box 360 controllers, which are readily available, and can be connected to a computer through a USB port. Many of the buttons in the GUI are linked to gamepad buttons, and the gamepad button will behave the same as the corresponding GUI button. As shown in the diagram above they are:

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ButtonFunction
    AStop
    BEnable Joystick
    XStart Choreography
    YStand
    Left-BumperSelf Right
    Right-BumperSit
    StartPower On
    BackPower Off

    When the joystick is enabled (either through hitting the “B” button on the gamepad or through the GUI button), the robot will walk, and can be driven by the joysticks. As shown in the diagram above, the left joystick controls translation, and the right joystick controls yaw. When the robot is controlled through any of the other Robot Controls buttons, when WASD mode is enabled, or when a dance routine has been started, joystick driving will be disabled however other buttons will still work.

    -

    While using Choreographer, the joystick controller mapping diagram can be accessed as a reminder using the menus Help->Joystick Controller Mapping.

    -

    Keyboard Controls

    -

    Similar to the joystick control, we provide the ability to drive the robot using the WASD keys on the keyboard. When enabled (either through hitting the GUI button or by pressing “v” on the keyboard), the robot will walk and can be driven using the WASD keys. Joystick mode will be disabled while driving in WASD mode. The hotkeys are setup to mimic the joystick button key presses when applicable. When the robot is controlled through any of the other Robot Controls buttons, when joystick mode is enabled, or when a dance routine has been started, the WASD driving will be disabled, however other keypresses will still be available.

    +

    Choreographer has specific hotkey mappings available to make common actions used when editing choreographies more easily accessible While using Choreographer, a table of available keystrokes can be accessed as a reminder using the menus Help->Hotkeys Documentation.

    @@ -756,69 +721,43 @@

    Keyboard Controls

    - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - + + -
    lPower OffEscExit "insert" mode
    yStandpPlay music
    xStart ChoreographyShift + ClickSelect multiple moves, adding each one to the selected group when clicked.
    [SitLeft/Right Arrow KeysNudge a move (or group of selected moves) left/right by one slice in the timeline. Cannot cross other moves with nudging; this can only be done when dragging a move (or group).
    ]Self-rightShift + Left/Right Arrow KeysExpand a move on the left/right side by one slice if possible. This only works when a single move is selected (and not a group of moves).
    wWalk forwardCtrl + Left/Right Arrow KeysShrink a move on the left/right side by one slice if possible. This only works when a single move is selected (and not a group of moves).
    aStrafe leftCtrl+CCopy the move (or group of selected moves).
    sWalk backwards
    dStrafe right
    qTurn left
    eTurn rightCtrl+VPaste the copied move (or group of selected moves).

    While using Choreographer, a table of available keystrokes can be accessed as a reminder using the menus Help->Hotkeys Documentation.

    -
    -
    -

    Restricted Mode

    -

    One of the goals for Choreographer is to provide a tool that gives the user as much freedom as they could possibly want. As such, you will be able to find combinations of moves, parameters, and BPM that Spot cannot reliably perform under all conditions. If, however, you want a more controlled experience that will be more likely to provide more reliable results, we have offered a Restricted Mode for Choreographer. To enable Restricted Mode, simply start Choreographer with an extra --restricted argument. Several of the more dynamic moves will be missing, and parameter ranges will generally be smaller, allowing for less energetic and generally more reliable dances.

    -

    Note that if you create a dance in normal mode, Choreographer may not be able to load it while in Restricted Mode if the dance’s parameters are outside its reduced range.

    -
    +

    diff --git a/docs/html/docs/concepts/choreography/choreographer_setup.html b/docs/html/docs/concepts/choreography/choreographer_setup.html index e38deb656..135d13285 100644 --- a/docs/html/docs/concepts/choreography/choreographer_setup.html +++ b/docs/html/docs/concepts/choreography/choreographer_setup.html @@ -8,7 +8,7 @@ - Install Choreographer — Spot 2.3.5 documentation + Install Choreographer — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -559,7 +600,7 @@ Development Kit License (20191101-BDSDK-SL). -->

    Install Choreographer

    -

    Choreographer is an application to easily author choreographies with advanced moves and parameters and execute the routines on robot with music synchronization. The application and choreography service require a special license to use. The application can be downloaded from the Support Center in the “Downloads Page” for the 2.1 Release. Additionally, the Support Center provides in depth documentation for how to use Choreographer to create routines, modify moves, connect to robots to execute routines, and debug issues with a FAQs section.

    +

    Choreographer is an application to easily author choreographies with advanced moves and parameters and execute the routines on robot with music synchronization. The application and choreography service require a special license to use. The application can be downloaded from the Support Center in the “Downloads Page” for the latest Spot software release. Additionally, the Support Center provides in depth documentation for how to use Choreographer to create routines, modify moves, connect to robots to execute routines, and debug issues with a FAQs section.

    System Requirements

    Choreographer supports 64-bit Microsoft Windows 10 and 64-bit Ubuntu 18.04 Linux. No other system dependencies are required to run the Choreographer application. However, to use the Choreography SDK independently from the Choreographer application, both python 3 and the bosdyn-choreography-protos and bosdyn-choreography-client wheels must be installed.

    @@ -571,7 +612,7 @@

    Installation and Running Choreographer
    sudo chmod +x choreographer
     
    -

    To run Choroeographer without any robots connected, just double-click on the executable to open it.

    +

    To run Choreographer without any robots connected, just double-click on the executable to open it.

    To run Choreographer with robots, start Choreographer from the command line, in the directory where the executable was download, with the following options:

    @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -563,12 +604,12 @@

    Choreography ServiceOverview

    What is it?

    -

    The Choreography service is a framework for producing precisely scripted motion, currently focused on dancing.

    -

    An example script can be seen on YouTube.

    +

    The Choreography service is a framework for producing precisely scripted motion, currently focused on dancing. Example choreography scripts can be seen on the Boston Dynamics Youtube channel, showing the robot dancing to Bruno Mars’s “Uptown Funk” and The Contours’ “Do You Love Me”.

    A choreography sequence consists of a series of moves. We can achieve a wide variety of possible behavior from a moderate list of available moves by:

    1. Combining multiple moves (see the tracks/layering section).

    2. Altering move parameters to vary the behavior of the move.

    3. +
    4. Adjusting the BPM of the choreography sequence to change the speed of the moves.

    @@ -587,19 +628,25 @@

    Slices

    Tracks/Layering

    -

    We divide the robot’s motion into four distinct tracks:

    +

    We divide the robot’s motion into the following distinct tracks:

    • Legs

    • Body

    • Arm

    • Gripper

    -

    Each dance move requires one or more of these tracks. Moves that use different tracks can be run simultaneously in any combination. In Choreographer, a track is represented as a horizontal section in the timeline view. For example, here is a screenshot from Choreographer of a script that combines moves in all three of the four tracks:

    -

    ../../../_images/main_image1.png

    +

    In addition to the base motion, there are also tracks for:

    +
      +
    • Lights: controls the robot’s front two sets of LEDs.

    • +
    • Annotations: enables dance annotations that are separate from any specific move.

    • +
    • (Choreographer Only) Music: controls the audio played from the Choreographer application when dancing.

    • +
    +

    Each dance move requires one or more of these tracks. Moves that use different tracks can be run simultaneously in any combination. In Choreographer, a track is represented as a horizontal section in the timeline view. For example, here is an image from Choreographer of a script that combines moves in three of the four tracks:

    +

    Tracks

    And the resulting behavior looks like this:

    -

    ../../../_images/main_image3.gif

    -

    Some moves require multiple tracks, such as the “Skip” move which uses the Body and Legs track or the “Arm Move Relative” which uses the Arm and Gripper tracks, as shown in this example:

    -

    ../../../_images/main_image4.png

    +

    Dancing Behavior Gif

    +

    Some moves require multiple tracks, such as the “Jump” move which uses the Body and Legs track or the “Arm Move” which uses the Arm and Gripper tracks, as shown in this example:

    +

    ../../../_images/multi_tracked_dance.png

    Entry/Exit conditions

    @@ -613,18 +660,45 @@

    Entry/Exit conditions

    The first leg-track move can have any entry state, and the robot will automatically transition to an acceptable entry state for that move before starting the choreography sequence. If the robot is not already in the correct starting pose, it may delay the start of the choreography sequence beyond the requested start time.

    All subsequent legs-track moves must have an entry state that corresponds to the previous legs-track move’s exit state. Scripts that violate this requirement will be rejected by the API and return warnings indicating which moves violate the entry/exit states. As well, routines made in Choreographer will highlight moves red when the entry state does not match the previous leg move’s exit state, such as in this example:

    -

    ../../../_images/main_image2.png

    +

    Transitions Error

    API

    +
    +

    Choreography API Interface

    The API defines a choreography sequence by a unique name, the number of slices per minute, and a repeated list of moves. Each move consists of the move’s type, its starting slice, duration (in slices), and the actual parameters (MoveParams proto message). The MoveParams message describe how the robot should behave during each move. For example, a move parameter could specify positions for the body. Each parameter may have specific limits/bounds that are described by the MoveInfo proto; this information can be found using the ListAllMoves RPC.

    Once a choreography sequence is created, the UploadChoreography RPC will send the routine to the robot. The choreography service will validate and check the structure of the routine to ensure it is feasible and within bounds.

    The service will return a list of warnings and failures related to the uploaded choreography sequence. A failure is something the choreography service could not automatically correct and must be fixed before the routine can be executed. A warning is something that could be automatically corrected for and won’t block the execution of the routine in certain scenarios. If the boolean non_strict_parsing is set to true in the UploadChoreography RPC, then the service will fix any correctable errors within the routine (ex. rounding down values the maximum allowed value) and allow a choreography sequence with warnings to be completed.

    The ExecuteChoreography RPC will run the choreography sequence to completion on the robot. A choreography sequence is identified by the unique name of the sequence that was uploaded to the robot. Additionally, a starting time (in robot’s time) and a starting slice will fully specify to the robot when to start the choreography sequence and at which move.

    +
    +
    +

    Animation API Interface

    +

    The API defines an animated move (Animation proto message) by a unique name, a repeated list of AnimationKeyFrames which describes the robot’s motion at each timestamp, and additional parameters and options which fully describe how the move should be executed. Unlike other dance moves, the information about the minimum and maximum parameters, if the move is extendable, or which tracks the move controls are not known to the robot beforehand and must be specified in the Animation protobuf.

    +

    The Animation can be uploaded to the robot using the UploadAnimatedMove RPC, which will send the animation to the robot. The choreography service will validate and check the structure of the animation to ensure it is fully specified and is feasible. If the animation does not pass this validation, the RPC will respond with a failure status and a set of warning messages indicating which parts of the animation failed.

    +

    If the animation uploads successfully, then it can be used within choreography sequences and will appear as a move option in Choreographer with any of the parameters that were specified in the initial Animation protobuf message. The move type associated with the uploaded animation is “animation::” + the animation’s name. The animations will persist on robot until either the robot is powered off or an animation with the exact same name is uploaded (overwriting the previous animation with that name).

    +

    While animations can be written manually using protobuf in any application, we have also provided a way to create animations from human-readable text files. The animation text file has the extension *.cha and has a specific format which is described in the animation file specification document. The animation *.cha file can be converted into an Animation protobuf message using the animation_file_to_proto.py script provided in the choreography client library.

    +
    +
    +

    Choreography Logs API Interface

    +

    The API defines a choreography log using the ChoreographyStateLog protobuf message, which consists of a repeated series of timestamped key frames that contain the joint state of the entire robot, the foot contacts, and the SE3Pose for the robot body relative to the animation frame. The animation frame is defined based on the feet position at the beginning of the animation: the position is the center of all four feet, and the rotation is yaw only as computed from the feet positions.

    +

    The choreography logs are divided into two types:

    +
      +
    • Automatic/”Last Choreography” Logs: This log is recorded from when the ExecuteChoreography RPC is first received to 3 seconds after the completion of the choreography.

    • +
    • Manual Logs: This log is recorded from when a StartRecordingState RPC is received to when a StopRecordingState RPC is received. There is a maximum of 5 minutes of recording for a manual log.

    • +
    +

    The choreography logs can be used to review how the robot actually executed a choreography or animated dance move. For example, specifically for animations, if the move is not completely feasible, the robot will attempt to get as close to what was asked as possible but may not succeed. The choreography log can be used to understand and update the animated move to be realistic.

    +

    The DownloadRobotStateLog RPC can be used to download the choreography logs. The request specifies which type of log should be downloaded. The response is streamed over grpc and recombined by the choreography client to create a full log message. The robot only keeps one auto log and one manual log in its buffer (2 total logs) at a time, so the log must be downloaded immediately after completing the move on robot.

    +
    +
    +

    Choreography Client

    The choreography service has a python client library which provides helper functions for each RPC as well as functions that help convert the choreography sequence from a protobuf message into either a binary or text file.

    +
    +
    +

    Python Examples using the Choreography API

    The upload_choreographed_sequence example demonstrates how to read an existing routine from a saved text file, upload it to the robot, and then execute the uploaded choreography.

    +

    Config Files

    There are two config files that describe the individual moves that are used by the Choreographer to compose different choreography sequences.

    @@ -645,6 +719,8 @@

    MoveInfoConfig.txt - Choreography Moves Reference — Spot 2.3.5 documentation + Choreography Moves Reference — Spot 3.0.0 documentation @@ -42,7 +42,7 @@ - + @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -745,6 +786,71 @@

    butt_circle +

    fidget_stand

    +

    A procedurally-generated idle animation. It looks around, breathes, shifts its weight, and stamps. Can use one of the preset configurations or customize the parameters.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ParameterEffect
    presetPre-designed parameter combinations that attempt to convey a specific emotion. NOTE: All other sliders are only active if this is set to "Custom".
    min_gaze_pitchHow far down it will look. (Radians)
    max_gaze_pitchHow far up it will look. (Radians)
    gaze_mean_periodHow frequently it will look somewhere else. (Seconds)
    gaze_center_cfpWhere the gaze array originates in the center-of-footprint frame. (Meters)
    shift_mean_periodHow frequently it will shift its weight. (Seconds)
    shift_max_transition_timeMaximum amount of time it will spend shifting its weight. (Seconds)
    breath_min_zMinimum amplitude of the "breathing". (Meters)
    breath_max_zMaximum amplitude of the "breathing". (Meters)
    leg_gesture_mean_periodHow frequently it will do a leg gesture. (Seconds)
    gaze_slew_rateHow quickly it will shift its gaze. (Meters/Second)
    gaze_position_generation_gainHow much Brownian motion will be applied to the gaze point.
    gaze_roll_generation_gainHow much Brownian motion will be applied to the gaze roll.

    Step Moves

    @@ -802,6 +908,40 @@

    step

    +
    +

    goto

    +

    ../../../_images/Goto.gif

    +

    Trot to a specified position in the dance frame. (Unless explicitly set, the dance frame is defined by where the dance began.) Takes 1 step per beat. Extend the duration to successfully move farther.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ParameterEffect
    absolute_positionPosition we should go to in the dance frame.
    absolute_yawYaw orientation we should go to in the dance frame.
    step_position_stiffnessHow precisely should we step in the nominal locations.
    duty_cycleWhat fraction of the time a foot is on the ground. 0.5 is a standard trot.
    link_to_nextShould we smoothly transition from this move to a subsequent goto move.

    trot

    ../../../_images/trot.gif

    @@ -859,12 +999,28 @@

    turn_2step

    crawl

    @@ -1100,12 +1272,28 @@

    jump¶ +translation_is_absolute +Is motion in the dance frame (true) or relative to the current position (false). + + +translation +How far to move. (Meters) + + +absolute_translation +Where to move to in the dance frame. (Meters) + + +yaw_is_absolute +Is yaw in the dance frame (true) or relative to the current position (false). + + yaw -What orientation to land in. +How far to rotate. (Radians) -absolute -If true, yaw is interpreted relative to the orientation the choreography sequence began in. If false, yaw is interpreted relative to the orientation before entering the move. +absolute_yaw +Where to rotate to in the dance frame. (Radians) flight_slices @@ -1116,6 +1304,10 @@

    jump¶ The footprint the robot should land its jump in. +swing_height +How how to pick up the feet. + + split_fraction Splits the liftoff and touchdown into two pairs of two legs. In fraction of of swing with two legs in flight, so 0 means all legs fully synchronized. @@ -1123,10 +1315,6 @@

    jump¶ lead_leg_pair If split_fraction is not 0, indicates which legs lift off first. Default AUTO mode will select a pair based on the translation vector. - -translation -Distance and direction the robot should jump. -

    @@ -1466,6 +1654,35 @@

    gripper +

    frame_snapshot

    +

    This move does not directly do anything. Instead it sets the frame to be used by future moves that are in an absolute frame. Some such moves will always be in the dance frame (frame_id = 0), and some allow you to explicitly select a frame_id. This move can set frames relative to either a fiducial or to the robot’s footprint.

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    ParameterEffect
    frame_idWhich frame to set. 0 is the dance frame and will be used by moves that do absolute motion but don't explicitly select a frame.
    fiducial_numberWhich fiducial to set the frame relative to. If set to -1 or the fiducial has not been seen recently, will set according to the robot's footprint.
    include_each_legShould each leg be included when determining the footprint.
    compensatedIf a foot is not included, should we offset the footprint as if that foot were in a nominal stance? Otherwise, we'll take the center of the included feet.

    chicken_head

    ../../../_images/chickenhead-opt.gif

    @@ -1493,6 +1710,158 @@

    chicken_head +

    Lights moves

    +

    All colors are specified as RGB on a 0-255 scale.

    +
    +

    set_color

    +

    Sets the color of the robot’s face lights. Can independently set left and right lights if desired. Can fade in and out gradually if desired.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ParameterEffect
    left_colorColor of the left lights. (Color of all lights if right_same_as_left specified.)
    right_same_as_leftIf true, all lights are left_color. If false, left and right are independently specified.
    right_colorColor of the right lights. Does nothing if right_same_as_left specified.
    fade_in_slicesHow long to spend brightening at the beginning, measured in slices (1/4 beats).
    fade_out_slicesHow long to spend darkening at the end, measured in slices (1/4 beats).
    +
    +

    fade_color

    +

    Sets the lights to show one color at the top fading towards another color at the bottom.

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    ParameterEffect
    top_colorThe color of the topmost lights.
    bottom_colorThe color of the bottommost lights.
    fade_in_slicesHow long to spend brightening at the beginning, measured in slices (1/4 beats).
    fade_out_slicesHow long to spend darkening at the end, measured in slices (1/4 beats).
    +
    +

    independent_Color

    +

    Independently specifies the color for all 8 of the lights.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ParameterEffect
    top_leftColor of the top left light.
    upper_mid_leftColor of the second from top left light.
    lower_mid_leftColor of the second from bottom left light.
    bottom_leftColor of the bottom left light.
    top_rightColor of the top right light.
    upper_mid_rightColor of the second from top right light.
    lower_mid_rightCColor of the second from bottom right light.
    bottom_rightColor of the bottom right light.
    fade_in_slicesHow long to spend brightening at the beginning, measured in slices (1/4 beats).
    fade_out_slicesHow long to spend darkening at the end, measured in slices (1/4 beats).
    +
    +

    ripple_Color

    +

    Sets the lights in a variety of moving patterns.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ParameterEffect
    mainColor to make the lights.
    secondarySecond color to make the lights, used only by some patterns.
    patternSelect from a variety of light motion patterns.
    light_sideWhich side lights to use for the pattern.
    increment_slicesHow quickly to move the pattern. Gives the time in slices (1/4 beats) between updates to the pattern.
    +

    diff --git a/docs/html/docs/concepts/choreography/robot_controls_in_choreographer.html b/docs/html/docs/concepts/choreography/robot_controls_in_choreographer.html new file mode 100644 index 000000000..08862bd12 --- /dev/null +++ b/docs/html/docs/concepts/choreography/robot_controls_in_choreographer.html @@ -0,0 +1,835 @@ + + + + + + + + + + + Connecting Robots to Choreographer — Spot 3.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + + + +
    + +
    + + + + + + + + + + + + + + + + + +
    + + + + +
    +
    +
    +
    + +
    +

    Connecting Robots to Choreographer

    +

    Robots can either be connected to Choreographer before starting the application, or they can be dynamically connected and disconnected from the application once it is running.

    +

    In order to connect your robot to Choreographer, you must start Choreographer from the command line and pass in the arguments --hostname {IP/Hostname of your Spot} --user {Username you use to log in to your Spot} --password {Password for your Spot}. If you wish to connect to multiple Spots at once, simply add more copies of those command line arguments, one set for each Spot.

    +

    To connect your robot after the application has been started, select the Robot Management Tab (shown in the image below), and then click the “Add” button. This will prompt you for the robot’s hostname, username, and then password. To remove any robot connections, click the “Disconnect” button and check any robots that you want to disconnect from. Note: disconnecting from a robot will power off the robot and release the lease and E-Stop.

    +

    Robot Management Tab

    +

    Choreographer supports connecting to multiple robots at once. In the Robot Management Tab, you can select which robots are being controlled at a given time. Additionally, you can apply different choreographies to each robot and start them all at the same time.

    +
    +

    Robot Controls

    +

    The Robot Controls bar is disabled if there are no robots connected to the Choreographer program. If a robot is connected, the buttons will have the following effects:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ButtonFunction
    Power OffPowers off the Spot’s motors. Always press this before approaching your Spot.
    Power OnPowers on your Spot’s motors. You must activate this before your Spot can stand or start choreography.
    E-StopEnables or disables your Spot’s E-Stop. In an emergency, use this to stop the Spot immediately.
    Self-RightIf your Spot has fallen, this will attempt to right it into a sitting position
    SitSits the Spot in-place. Cancels all current choreography and music, but E-Stop or Power Off should be used in an emergency.
    StandBrings Spot to a stand. Cancels all current choreography and music, but E-Stop or Power Off should be used in an emergency.
    Enable JoystickActivated Joystick Controls (see Joystick Controls section)
    Enable WASD DrivingActivates "WASD" keyboard driving (see WASD Controls section)
    Return To StartSpot will walk from its current location to the last location it started a dance (either a full choreography or the move preview dance).
    Start ChoreographySends your choreography sequence to your Spot, then starts a 3 second countdown before the robot begins dancing. Any loaded music will automatically start as soon as Spot begins to dance.

    Note: The return to start button (added in the Spot 2.4 release) will bring all selected robots back to their starting position after completing a choreography. If multiple robots are being controlled, there is obstacle avoidance enabled when they are navigating back to the starting position. To adjust this obstacle padding distance (in meters), the command line argument --obs-padding DIST_IN_METERS can be provided when starting the Choreographer application.

    +
    +
    +

    Joystick Controls

    +

    Joystick Controls

    +

    A X-Box gamepad controller can be used with the GUI for convenience of moving and positioning the robot. The button layout is set up for X-Box 360 controllers, which are readily available, and can be connected to a computer through a USB port. Many of the buttons in the GUI are linked to gamepad buttons, and the gamepad button will behave the same as the corresponding GUI button. As shown in the diagram above they are:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ButtonFunction
    AStop
    BEnable Joystick
    XStart Choreography
    YStand
    Left-BumperSelf Right
    Right-BumperSit
    StartPower On
    BackPower Off

    When the joystick is enabled (either through hitting the “B” button on the gamepad or through the GUI button), the robot will walk, and can be driven by the joysticks. As shown in the diagram above, the left joystick controls translation, and the right joystick controls yaw. When the robot is controlled through any of the other Robot Controls buttons, when WASD mode is enabled, or when a dance routine has been started, joystick driving will be disabled however other buttons will still work.

    +

    While using Choreographer, the joystick controller mapping diagram can be accessed as a reminder using the menus Help->Joystick Controller Mapping.

    +

    Note: the X-Box controller must be connected to the computer via USB port before opening the Choreographer application.

    +
    +
    +

    Keyboard Controls

    +

    Similar to the joystick control, we provide the ability to drive the robot using the WASD keys on the keyboard. When enabled (either through hitting the GUI button or by pressing “v” on the keyboard), the robot will walk and can be driven using the WASD keys. Joystick mode will be disabled while driving in WASD mode. The hotkeys are setup to mimic the joystick button key presses when applicable. When the robot is controlled through any of the other Robot Controls buttons, when joystick mode is enabled, or when a dance routine has been started, the WASD driving will be disabled, however other keypresses will still be available.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    KeyFunction
    vEnable WASD mode
    bEnable Joystick mode
    kPower On
    lPower Off
    yStand
    xStart Choreography
    [Sit
    ]Self-right
    wWalk forward
    aStrafe left
    sWalk backwards
    dStrafe right
    qTurn left
    eTurn right

    While using Choreographer, a table of available keystrokes can be accessed as a reminder using the menus Help->Hotkeys Documentation.

    +
    +
    + + +
    + +
    + +
    +
    + +
    + +
    + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/html/docs/concepts/data.html b/docs/html/docs/concepts/data.html index b5af84d26..3ae38808f 100644 --- a/docs/html/docs/concepts/data.html +++ b/docs/html/docs/concepts/data.html @@ -8,7 +8,7 @@ - Spot Data — Spot 2.3.5 documentation + Spot Data — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/data_acquisition_output.html b/docs/html/docs/concepts/data_acquisition_output.html index 597a21c41..d4ec51483 100644 --- a/docs/html/docs/concepts/data_acquisition_output.html +++ b/docs/html/docs/concepts/data_acquisition_output.html @@ -8,7 +8,7 @@ - Data Acquisition Output — Spot 2.3.5 documentation + Data Acquisition Output — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/data_acquisition_overview.html b/docs/html/docs/concepts/data_acquisition_overview.html index 9ff136e65..e00aa3456 100644 --- a/docs/html/docs/concepts/data_acquisition_overview.html +++ b/docs/html/docs/concepts/data_acquisition_overview.html @@ -8,7 +8,7 @@ - Data Acquisition Overview — Spot 2.3.5 documentation + Data Acquisition Overview — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/data_buffer_overview.html b/docs/html/docs/concepts/data_buffer_overview.html index d9c1f37ef..cf6b42ad9 100644 --- a/docs/html/docs/concepts/data_buffer_overview.html +++ b/docs/html/docs/concepts/data_buffer_overview.html @@ -8,7 +8,7 @@ - Data Buffer Overview — Spot 2.3.5 documentation + Data Buffer Overview — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/developing_api_services.html b/docs/html/docs/concepts/developing_api_services.html index ac6f14d08..ffff899ba 100644 --- a/docs/html/docs/concepts/developing_api_services.html +++ b/docs/html/docs/concepts/developing_api_services.html @@ -8,7 +8,7 @@ - Developing API Services — Spot 2.3.5 documentation + Developing API Services — Spot 3.0.0 documentation @@ -42,7 +42,7 @@ - + @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -593,7 +634,7 @@

    Starting a Serverservice_runner.run_until_interrupt() -

    The image_service_pb2_grpc.add_ImageServiceServicer_to_server is a function auto generated from a protobuf service definition. The function links a servicer to a server and is generated for every protobuf server compiled. The WebCamImageServicer is a custom class that inherits from the servicer class auto generated from the protobuf service definition. It defines methods for responding to all possible service requests. The GrpcServiceRunner class will create and run a server object associated with the passed in servicer. It will monitor for requests at the given port. The run_until_interrupt() method can be used to keep a server alive until it receives a SIGINT.

    +

    The image_service_pb2_grpc.add_ImageServiceServicer_to_server is a function auto generated from a protobuf service definition. The function links a servicer to a server and is generated for every protobuf server compiled. The WebCamImageServicer is a custom class that inherits from the servicer class auto generated from the protobuf service definition. It defines methods for responding to all possible service requests. The GrpcServiceRunner class will create and run a server object associated with the passed in servicer. It will monitor for requests at the given port. The run_until_interrupt() method can be used to keep a server alive until it receives a SIGINT.

    Registering a Service

    diff --git a/docs/html/docs/concepts/estop_service.html b/docs/html/docs/concepts/estop_service.html index b949072ac..a98379abe 100644 --- a/docs/html/docs/concepts/estop_service.html +++ b/docs/html/docs/concepts/estop_service.html @@ -8,7 +8,7 @@ - E-Stop Service — Spot 2.3.5 documentation + E-Stop Service — Spot 3.0.0 documentation @@ -41,7 +41,7 @@ - + @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/faults.html b/docs/html/docs/concepts/faults.html index a3dd693c1..751e59352 100644 --- a/docs/html/docs/concepts/faults.html +++ b/docs/html/docs/concepts/faults.html @@ -8,7 +8,7 @@ - Faults — Spot 2.3.5 documentation + Faults — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/geometry_and_frames.html b/docs/html/docs/concepts/geometry_and_frames.html index 9ba1e5ca4..4cff19ea5 100644 --- a/docs/html/docs/concepts/geometry_and_frames.html +++ b/docs/html/docs/concepts/geometry_and_frames.html @@ -8,7 +8,7 @@ - Geometry and Frames — Spot 2.3.5 documentation + Geometry and Frames — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/lease_service.html b/docs/html/docs/concepts/lease_service.html new file mode 100644 index 000000000..8b0e230ca --- /dev/null +++ b/docs/html/docs/concepts/lease_service.html @@ -0,0 +1,698 @@ + + + + + + + + + + + Lease Service — Spot 3.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + + + +
    + +
    + + + + + + + + + + + + + + + + + +
    + +
      + +
    • »
    • + +
    • Concepts »
    • + +
    • Lease Service
    • + + +
    • + + + +
    • + +
    + + +
    +
    +
    +
    + +
    +

    Lease Service

    +

    Leases represent ownership of the robot for the purpose of issuing commands to control the robot. There can only ever be a single owner of a specific robot resource at any given time, however an application can delegate the lease to other systems to perform various tasks. Leases can be returned by one application and subsequently acquired by a different application to change ownership. Alternatively, a lease can be revoked by another application to change ownership forcefully.

    +

    In addition to identifying ownership, the lease system is used to confirm that a robot is still being controlled and that there are valid communications to the owner. If an owner can no longer be reached within a certain period of time, the lease will automatically be revoked from that owner.

    +
    +

    Basic Lease Usage

    +

    Services which command or control the robot require a lease. These services will reject incoming requests which are from non-owners or contain older ownership information. Ownership semantics provides additional safety to operation workflows by preventing accidental commands from disturbing the current owner’s application. Note, some services will only read information from the robot, and do not require a lease (e.g. image service or robot-state service).

    +

    Applications which want to control the robot will begin by acquiring a lease to the robot. A lease can be acquired with the AcquireLease RPC when it has no existing owner.

    +

    Throughout the duration of the application, the client should continue to send “keep-alive” signals to continue ownership of the robot’s resources. The “keep-alive” signal is sent as the RetainLease RPC. If enough time passes between the last retain lease request received, the robot will revoke ownership of the lease such that other applications can acquire ownership.

    +

    When issuing robot commands, the robot will be sent a version of the lease with the specific command to permit the robot to complete the desired command. This lease will automatically be included with the command request when using the SDK

    +

    Finally, when the application is complete, it should return the lease. The ReturnLease RPC should be used when a client is finished with their ownership to indicate that the robot is no longer owned without waiting for a revocation timeout. Note, applications should only return the lease when it was the one who acquired the lease using the AcquireLease RPC. In the case of a remote mission service callback or other services which are delegated the lease by a different main service, the application’s service should not return the lease when finished - the main service acquired the lease and will return it when complete.

    +
    +
    +

    [Advanced] Lease Resources

    +

    A lease can claim ownership of the entire robot or specific resources. The “body” resource describes ownership of the entire robot, and can be broken down into sub-resources for the arm, gripper, and mobility (controlling only the legs), as shown in the image below. In most use cases, an application simply needs to send the “body” lease and the robot will determine which specific resources it needs for the command being issued.

    +

    Resource Tree

    +

    The different robot resources create a tree-structured hierarchy such that the client can control a subset of the robot or the entire robot without needing to manage individual resources. Owning a lease with a parent resource represents control over the entire subtree.

    +
    +
    +

    [Advanced] Additional Lease Usage Patterns

    +

    Typically, the lease and ownership can be gained using the AcquireLease RPC> If the lease is actively being owned, the TakeLease RPC provides an alternative that will forcefully take ownership of the lease resources to give control to the application issuing the RPC. This should only be used expressly by a human who is aware of what they’re taking control from.

    +

    When issuing a specific command which requires a lease, the robot should be sent a sub-lease of the main, acquired lease. This delegates the ownership of that lease’s resources to the particular service being sent the command. Note, the creation of a sub-lease for a specific command is done automatically by the client library. Commands which require a lease will respond with LeaseUseResult protos, which reflect whether or not the lease provides proper ownership of the robot.

    +

    Additionally, clients should simply send the “body” lease and the robot service’s will split the lease into only the necessary parts. This allows clients to issue multiple commands which control different resources (e.x. a gripper command and a body trajectory command) without worrying about breaking apart a lease themselves. Clients can split leases into sub-resources, but will need to be careful to preserve the sequence number.

    +
    +
    +

    [Advanced] Lease Representation

    +

    A lease contains the resource which it controls ownership, a sequence, an epoch, and set of client names.

    +

    The sequence is a list of integer numbers which indicates when the lease was generated and if it is a sub-lease. The first number in the sequence is the root lease number, and is generated by the base lease service. Sequence numbers are incremented when creating new leases. Additional sequence numbers can be appended to the lease sequence when creating a sub-lease to delegate ownership to a specific service. For example, the lease sequence lease=[3, 1] has a root lease number of 3, and a single sub-lease with value 1.

    +

    Resource ownership is determined by whichever lease has the highest root lease number. For example, when comparing leaseA = [6, 1] and leaseB = [5, 13], leaseA is considered newer and the current owner.

    +

    In addition to ownership, the lease sequences are used by services to determine which command should be executing currently. Services will use and execute whichever command shows ownership of the necessary resources and has the newest lease sequence. To compare leases, the sequence number with the first higher number is considered the newest lease. For example, when comparing leaseA = [1, 2, 10] and leaseB = [1, 2, 11], leaseB is considered newer since the second sequence number is higher in leaseB than in leaseA.

    +

    The epoch is used to create a scope for the sequence field, such that only leases with the same epoch can be compared. The client names are a list of each client which has held the lease; these can be helpful for debugging to see which services were delegated ownership and created sub-leases.

    +

    Consider the example below.

    +

    Lease Usage Example

    +

    A client application took the lease from the tablet (or the tablet lost communication) and now owns the robot. The client application delegates control to graph-nav by creating a sub-lease, and graph-nav again creates a sub-lease to give control to the robot command service. When the tablet attempts to issue a robot command, it will be rejected because it is now considered older.

    +
    +
    +

    [Advanced] Understanding Lease Errors

    +

    When a command is sent including a lease, the service checks the following things before accepting the command:

    +
      +
    • The incoming lease is valid: contains the current epoch, has known robot resources and the resources are correct for this type of command, has a non-empty sequence with a root lease number which has been issued by the lease service.

    • +
    • The incoming lease shows ownership as the newest lease: compares the incoming lease with the service’s current known lease.

    • +
    +

    The service will then respond to the command with a LeaseUseResult proto that indicates whether the incoming lease passed the checks and includes additional information to aid in debugging.

    +

    When a command with a lease fails with a lease error, the status within the lease use result provides the root-cause of the failure. In addition to the status, the LeaseUseResult proto contains information about the current lease owner of the incoming lease’s resource, the most recent lease known to the system for the incoming lease’s resource, the latest leases for each leaf resource (since these can be different depending on the types of commands sent). This information can be used to react to the lease failure within the client application.

    +
    +
    + + +
    + +
    + +
    +
    + +
    + +
    + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/html/docs/concepts/network_compute_bridge.html b/docs/html/docs/concepts/network_compute_bridge.html index 1eaeba506..8988e7142 100644 --- a/docs/html/docs/concepts/network_compute_bridge.html +++ b/docs/html/docs/concepts/network_compute_bridge.html @@ -8,7 +8,7 @@ - Machine Learning Bridge and External Compute — Spot 2.3.5 documentation + Machine Learning Bridge and External Compute — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/networking.html b/docs/html/docs/concepts/networking.html index 7c9aa5937..65f8e3701 100644 --- a/docs/html/docs/concepts/networking.html +++ b/docs/html/docs/concepts/networking.html @@ -8,7 +8,7 @@ - Networking — Spot 2.3.5 documentation + Networking — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/concepts/robot_services.html b/docs/html/docs/concepts/robot_services.html index 7fa9f1922..8fbe73717 100644 --- a/docs/html/docs/concepts/robot_services.html +++ b/docs/html/docs/concepts/robot_services.html @@ -8,7 +8,7 @@ - Robot Services — Spot 2.3.5 documentation + Robot Services — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -613,7 +654,7 @@

    robot-state

    image

    Spot can have many different image sources, including the cameras on the base platform or any other payloads which implement the image service proto definitions, like Spot CAM. The image service provides a way to list all these different sources using the ListImageSources RPC and then query the sources for their images with the GetImage RPC.

    -

    Images can be regular pixel-based visual images where the data value corresponds to the greyscale (or color) intensity. They can also be depth images where the data value corresponds to the depth measured from the camera sensor. To align depth data with visual image data, use the depth_in_visual_frame sources, which reprojects the depth onto the same projection as the visual image.

    +

    Images can be regular pixel-based visual images where the data value corresponds to the greyscale (or color) intensity. They can also be depth images where the data value corresponds to the depth measured from the camera sensor. To align depth data with visual image data, use the depth_in_visual_frame sources (example code), which reprojects the depth onto the same projection as the visual image.

    Since an image can be a lot of data, there are also different types of encodings and compression schemes that the image can be transmitted as to reduce the size of the data sent over the wire. The image service offers a Format field to describe the encoding of the image:

    @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/payload/README.html b/docs/html/docs/payload/README.html index e0ae9c1ff..d58f8be19 100644 --- a/docs/html/docs/payload/README.html +++ b/docs/html/docs/payload/README.html @@ -8,7 +8,7 @@ - Payload Developer Guide — Spot 2.3.5 documentation + Payload Developer Guide — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/payload/configuring_payload_software.html b/docs/html/docs/payload/configuring_payload_software.html index 7ed050fef..e5819ac43 100644 --- a/docs/html/docs/payload/configuring_payload_software.html +++ b/docs/html/docs/payload/configuring_payload_software.html @@ -8,7 +8,7 @@ - Payload Software Interface — Spot 2.3.5 documentation + Payload Software Interface — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -757,7 +798,7 @@

    Payload port forwarding table - Running Custom Applications with Spot — Spot 2.3.5 documentation + Running Custom Applications with Spot — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/payload/guidelines_for_robust_payload_design.html b/docs/html/docs/payload/guidelines_for_robust_payload_design.html index 1b7e273e7..a76729eba 100644 --- a/docs/html/docs/payload/guidelines_for_robust_payload_design.html +++ b/docs/html/docs/payload/guidelines_for_robust_payload_design.html @@ -8,7 +8,7 @@ - Guidelines for Robust Payload Design — Spot 2.3.5 documentation + Guidelines for Robust Payload Design — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/payload/mechanical_interfaces.html b/docs/html/docs/payload/mechanical_interfaces.html index df5e05173..f321591e6 100644 --- a/docs/html/docs/payload/mechanical_interfaces.html +++ b/docs/html/docs/payload/mechanical_interfaces.html @@ -8,7 +8,7 @@ - Mechanical Interfaces — Spot 2.3.5 documentation + Mechanical Interfaces — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/payload/payload_configuration_requirements.html b/docs/html/docs/payload/payload_configuration_requirements.html index 5990c3f75..3e395a64b 100644 --- a/docs/html/docs/payload/payload_configuration_requirements.html +++ b/docs/html/docs/payload/payload_configuration_requirements.html @@ -8,7 +8,7 @@ - Configuration Requirements — Spot 2.3.5 documentation + Configuration Requirements — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/payload/robot_electrical_interface.html b/docs/html/docs/payload/robot_electrical_interface.html index e55dd76aa..51c38050e 100644 --- a/docs/html/docs/payload/robot_electrical_interface.html +++ b/docs/html/docs/payload/robot_electrical_interface.html @@ -8,7 +8,7 @@ - Electrical Interface — Spot 2.3.5 documentation + Electrical Interface — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/payload/robot_mounting_rails.html b/docs/html/docs/payload/robot_mounting_rails.html index 69bce37da..ac45207e1 100644 --- a/docs/html/docs/payload/robot_mounting_rails.html +++ b/docs/html/docs/payload/robot_mounting_rails.html @@ -8,7 +8,7 @@ - Mounting Rails — Spot 2.3.5 documentation + Mounting Rails — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/docs/payload/spot_core_cockpit.html b/docs/html/docs/payload/spot_core_cockpit.html index 400d2fadb..7091955fd 100644 --- a/docs/html/docs/payload/spot_core_cockpit.html +++ b/docs/html/docs/payload/spot_core_cockpit.html @@ -8,7 +8,7 @@ - Spot CORE Cockpit - System Management Tool — Spot 2.3.5 documentation + Spot CORE Cockpit - System Management Tool — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -616,13 +657,13 @@

    Spot CORE WiFi Network Settingsnmcli d wifi list # List available WiFi networks. -

    From the listed wireless access points, replace the captilized letters of the command below with your desired WiFi network and password.

    +

    From the listed wireless access points, replace the capitalized letters of the command below with your desired WiFi network and password.

    sudo nmcli d wifi connect "MY_WIFI" password "MY_PASSWORD"
     

    Included below is an example of running the above commands. login

    -

    A succesful connection will appear as follows.

    +

    A successful connection will appear as follows.

    login

    However, to test the internet connection, attempt a ping to google.com. Press Crtl + C to exit.

    login

    @@ -633,7 +674,7 @@

    Spot CORE WiFi Network Settings

    IMPORTANT NOTES:

      -
    1. By default, Boston Dynamics has included 2 pre-defined Routes which will route outbound communication from the Spot CORE to the explicity defined gateways, Spot’s Access Point 192.168.80.0 and Spot’s Ethernet port 10.0.0.0, rather than the default gateway.

    2. +
    3. By default, Boston Dynamics has included 2 pre-defined Routes which will route outbound communication from the Spot CORE to the explicitly defined gateways, Spot’s Access Point 192.168.80.0 and Spot’s Ethernet port 10.0.0.0, rather than the default gateway.

    4. These routes are only required if the default gateway is removed for internet access.

    5. The route to 10.0.0.0 is only valid if the user has not adjusted the Ethernet network settings on the robot admin console.

    6. To access the internet, remove the default gateway (the third field) under Addresses as pictured below and Apply these changes. Instead of routing all traffic to the robot, we will instead route to the internet.

    7. diff --git a/docs/html/docs/payload/spot_core_vnc.html b/docs/html/docs/payload/spot_core_vnc.html index b313bcdeb..bfa0d4586 100644 --- a/docs/html/docs/payload/spot_core_vnc.html +++ b/docs/html/docs/payload/spot_core_vnc.html @@ -8,7 +8,7 @@ - Spot CORE VNC — Spot 2.3.5 documentation + Spot CORE VNC — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
      - 2.3.5 + 3.0.0
      @@ -102,6 +102,7 @@
    8. Geometry and Frames
    9. Robot services
    10. E-Stop
    11. +
    12. Lease
    13. Developing API Services
    14. Faults
    15. Autonomy services
    16. Choreography
    17. Spot Arm
    18. Robot Behavior and Commands Examples
    19. Arm Command Examples
    20. Logging Examples
    21. Autonomy and Missions Examples
    22. @@ -245,6 +271,7 @@
    23. Arm Surface Contact
    24. Async Tasks
    25. Auth
    26. +
    27. Auto Return
    28. BDDF
    29. BDDF Download
    30. Channel
    31. @@ -268,12 +295,14 @@
    32. Graph Nav
    33. Image
    34. Image Service Helpers
    35. +
    36. IR Enable/Disable
    37. Lease
    38. License
    39. Local Grid
    40. Log Annotation
    41. Math Helpers
    42. Manipulation API
    43. +
    44. Map Processing
    45. Network Compute Bridge
    46. Payload Registration
    47. Payload
    48. @@ -321,6 +350,7 @@
    49. GRPC Reader
    50. GRPC Service Reader
    51. GRPC Service Writer
    52. +
    53. Message Reader
    54. POD Series Reader
    55. POD Series Writer
    56. Protobuf Channel Reader
    57. @@ -344,6 +374,8 @@
    58. Choreography
    59. @@ -377,6 +409,8 @@
    60. arm_surface_contact_service.proto
    61. auth.proto
    62. auth_service.proto
    63. +
    64. auto_return/auto_return.proto
    65. +
    66. auto_return/auto_return_service.proto
    67. basic_command.proto
    68. bddf.proto
    69. data_acquisition.proto
    70. @@ -403,6 +437,8 @@
    71. graph_nav/graph_nav.proto
    72. graph_nav/graph_nav_service.proto
    73. graph_nav/map.proto
    74. +
    75. graph_nav/map_processing.proto
    76. +
    77. graph_nav/map_processing_service.proto
    78. graph_nav/nav.proto
    79. graph_nav/recording.proto
    80. graph_nav/recording_service.proto
    81. @@ -410,6 +446,8 @@
    82. header.proto
    83. image.proto
    84. image_service.proto
    85. +
    86. ir_enable_disable.proto
    87. +
    88. ir_enable_disable_service.proto
    89. lease.proto
    90. lease_service.proto
    91. license.proto
    92. @@ -429,6 +467,7 @@
    93. mobility_command.proto
    94. network_compute_bridge.proto
    95. network_compute_bridge_service.proto
    96. +
    97. network_stats.proto
    98. parameter.proto
    99. payload.proto
    100. payload_estimation.proto
    101. @@ -446,6 +485,7 @@
    102. robot_state.proto
    103. robot_state_service.proto
    104. service_fault.proto
    105. +
    106. sparse_features.proto
    107. spot/door.proto
    108. spot/door_service.proto
    109. spot/robot_command.proto
    110. @@ -463,6 +503,7 @@
    111. spot_cam/service.proto
    112. spot_cam/streamquality.proto
    113. spot_cam/version.proto
    114. +
    115. stairs.proto
    116. synchronized_command.proto
    117. time_range.proto
    118. time_sync.proto
    119. @@ -681,7 +722,7 @@

      Start vncserver

      Enable vncserver on boot

      -

      In order for vncserver to automatically run on Spot CORE whenever Spot is turned on, use a systemd .service file. Create a servive file named vncserver@.service and add the following contents. This file can take a port number as an argument.

      +

      In order for vncserver to automatically run on Spot CORE whenever Spot is turned on, use a systemd .service file. Create a service file named vncserver@.service and add the following contents. This file can take a port number as an argument.

      [Unit]
       Description=TigerVNC server
       After=network.target
      diff --git a/docs/html/docs/protos/README.html b/docs/html/docs/protos/README.html
      index 11a72a141..fda800822 100644
      --- a/docs/html/docs/protos/README.html
      +++ b/docs/html/docs/protos/README.html
      @@ -8,7 +8,7 @@
       
         
         
      -  API Protocol — Spot 2.3.5 documentation
      +  API Protocol — Spot 3.0.0 documentation
         
       
         
      @@ -69,7 +69,7 @@
                   
                   
                     
      - 2.3.5 + 3.0.0
      @@ -102,6 +102,7 @@
    120. Geometry and Frames
    121. Robot services
    122. E-Stop
    123. +
    124. Lease
    125. Developing API Services
    126. Faults
    127. Autonomy services
    128. Choreography
    129. Spot Arm
    130. Robot Behavior and Commands Examples
    131. Arm Command Examples
    132. Logging Examples
    133. Autonomy and Missions Examples
    134. @@ -245,6 +271,7 @@
    135. Arm Surface Contact
    136. Async Tasks
    137. Auth
    138. +
    139. Auto Return
    140. BDDF
    141. BDDF Download
    142. Channel
    143. @@ -268,12 +295,14 @@
    144. Graph Nav
    145. Image
    146. Image Service Helpers
    147. +
    148. IR Enable/Disable
    149. Lease
    150. License
    151. Local Grid
    152. Log Annotation
    153. Math Helpers
    154. Manipulation API
    155. +
    156. Map Processing
    157. Network Compute Bridge
    158. Payload Registration
    159. Payload
    160. @@ -321,6 +350,7 @@
    161. GRPC Reader
    162. GRPC Service Reader
    163. GRPC Service Writer
    164. +
    165. Message Reader
    166. POD Series Reader
    167. POD Series Writer
    168. Protobuf Channel Reader
    169. @@ -344,6 +374,8 @@
    170. Choreography
    171. @@ -377,6 +409,8 @@
    172. arm_surface_contact_service.proto
    173. auth.proto
    174. auth_service.proto
    175. +
    176. auto_return/auto_return.proto
    177. +
    178. auto_return/auto_return_service.proto
    179. basic_command.proto
    180. bddf.proto
    181. data_acquisition.proto
    182. @@ -403,6 +437,8 @@
    183. graph_nav/graph_nav.proto
    184. graph_nav/graph_nav_service.proto
    185. graph_nav/map.proto
    186. +
    187. graph_nav/map_processing.proto
    188. +
    189. graph_nav/map_processing_service.proto
    190. graph_nav/nav.proto
    191. graph_nav/recording.proto
    192. graph_nav/recording_service.proto
    193. @@ -410,6 +446,8 @@
    194. header.proto
    195. image.proto
    196. image_service.proto
    197. +
    198. ir_enable_disable.proto
    199. +
    200. ir_enable_disable_service.proto
    201. lease.proto
    202. lease_service.proto
    203. license.proto
    204. @@ -429,6 +467,7 @@
    205. mobility_command.proto
    206. network_compute_bridge.proto
    207. network_compute_bridge_service.proto
    208. +
    209. network_stats.proto
    210. parameter.proto
    211. payload.proto
    212. payload_estimation.proto
    213. @@ -446,6 +485,7 @@
    214. robot_state.proto
    215. robot_state_service.proto
    216. service_fault.proto
    217. +
    218. sparse_features.proto
    219. spot/door.proto
    220. spot/door_service.proto
    221. spot/robot_command.proto
    222. @@ -463,6 +503,7 @@
    223. spot_cam/service.proto
    224. spot_cam/streamquality.proto
    225. spot_cam/version.proto
    226. +
    227. stairs.proto
    228. synchronized_command.proto
    229. time_range.proto
    230. time_sync.proto
    231. diff --git a/docs/html/docs/protos/style_guide.html b/docs/html/docs/protos/style_guide.html index d164ff682..5b500c3dd 100644 --- a/docs/html/docs/protos/style_guide.html +++ b/docs/html/docs/protos/style_guide.html @@ -8,7 +8,7 @@ - Boston Dynamics API Protobuf Guidelines — Spot 2.3.5 documentation + Boston Dynamics API Protobuf Guidelines — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
      - 2.3.5 + 3.0.0
      @@ -102,6 +102,7 @@
    232. Geometry and Frames
    233. Robot services
    234. E-Stop
    235. +
    236. Lease
    237. Developing API Services
    238. Faults
    239. Autonomy services
    240. Choreography
    241. Spot Arm
    242. Robot Behavior and Commands Examples
    243. Arm Command Examples
    244. Logging Examples
    245. Autonomy and Missions Examples
    246. @@ -245,6 +271,7 @@
    247. Arm Surface Contact
    248. Async Tasks
    249. Auth
    250. +
    251. Auto Return
    252. BDDF
    253. BDDF Download
    254. Channel
    255. @@ -268,12 +295,14 @@
    256. Graph Nav
    257. Image
    258. Image Service Helpers
    259. +
    260. IR Enable/Disable
    261. Lease
    262. License
    263. Local Grid
    264. Log Annotation
    265. Math Helpers
    266. Manipulation API
    267. +
    268. Map Processing
    269. Network Compute Bridge
    270. Payload Registration
    271. Payload
    272. @@ -321,6 +350,7 @@
    273. GRPC Reader
    274. GRPC Service Reader
    275. GRPC Service Writer
    276. +
    277. Message Reader
    278. POD Series Reader
    279. POD Series Writer
    280. Protobuf Channel Reader
    281. @@ -344,6 +374,8 @@
    282. Choreography
    283. @@ -377,6 +409,8 @@
    284. arm_surface_contact_service.proto
    285. auth.proto
    286. auth_service.proto
    287. +
    288. auto_return/auto_return.proto
    289. +
    290. auto_return/auto_return_service.proto
    291. basic_command.proto
    292. bddf.proto
    293. data_acquisition.proto
    294. @@ -403,6 +437,8 @@
    295. graph_nav/graph_nav.proto
    296. graph_nav/graph_nav_service.proto
    297. graph_nav/map.proto
    298. +
    299. graph_nav/map_processing.proto
    300. +
    301. graph_nav/map_processing_service.proto
    302. graph_nav/nav.proto
    303. graph_nav/recording.proto
    304. graph_nav/recording_service.proto
    305. @@ -410,6 +446,8 @@
    306. header.proto
    307. image.proto
    308. image_service.proto
    309. +
    310. ir_enable_disable.proto
    311. +
    312. ir_enable_disable_service.proto
    313. lease.proto
    314. lease_service.proto
    315. license.proto
    316. @@ -429,6 +467,7 @@
    317. mobility_command.proto
    318. network_compute_bridge.proto
    319. network_compute_bridge_service.proto
    320. +
    321. network_stats.proto
    322. parameter.proto
    323. payload.proto
    324. payload_estimation.proto
    325. @@ -446,6 +485,7 @@
    326. robot_state.proto
    327. robot_state_service.proto
    328. service_fault.proto
    329. +
    330. sparse_features.proto
    331. spot/door.proto
    332. spot/door_service.proto
    333. spot/robot_command.proto
    334. @@ -463,6 +503,7 @@
    335. spot_cam/service.proto
    336. spot_cam/streamquality.proto
    337. spot_cam/version.proto
    338. +
    339. stairs.proto
    340. synchronized_command.proto
    341. time_range.proto
    342. time_sync.proto
    343. diff --git a/docs/html/docs/python/README.html b/docs/html/docs/python/README.html index 50dcc4af0..7c32ed010 100644 --- a/docs/html/docs/python/README.html +++ b/docs/html/docs/python/README.html @@ -8,7 +8,7 @@ - Python Library — Spot 2.3.5 documentation + Python Library — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
      - 2.3.5 + 3.0.0
      @@ -102,6 +102,7 @@
    344. Geometry and Frames
    345. Robot services
    346. E-Stop
    347. +
    348. Lease
    349. Developing API Services
    350. Faults
    351. Autonomy services
    352. Choreography
    353. Spot Arm
    354. Robot Behavior and Commands Examples
    355. Arm Command Examples
    356. Logging Examples
    357. Autonomy and Missions Examples
    358. @@ -245,6 +271,7 @@
    359. Arm Surface Contact
    360. Async Tasks
    361. Auth
    362. +
    363. Auto Return
    364. BDDF
    365. BDDF Download
    366. Channel
    367. @@ -268,12 +295,14 @@
    368. Graph Nav
    369. Image
    370. Image Service Helpers
    371. +
    372. IR Enable/Disable
    373. Lease
    374. License
    375. Local Grid
    376. Log Annotation
    377. Math Helpers
    378. Manipulation API
    379. +
    380. Map Processing
    381. Network Compute Bridge
    382. Payload Registration
    383. Payload
    384. @@ -321,6 +350,7 @@
    385. GRPC Reader
    386. GRPC Service Reader
    387. GRPC Service Writer
    388. +
    389. Message Reader
    390. POD Series Reader
    391. POD Series Writer
    392. Protobuf Channel Reader
    393. @@ -344,6 +374,8 @@
    394. Choreography
    395. @@ -377,6 +409,8 @@
    396. arm_surface_contact_service.proto
    397. auth.proto
    398. auth_service.proto
    399. +
    400. auto_return/auto_return.proto
    401. +
    402. auto_return/auto_return_service.proto
    403. basic_command.proto
    404. bddf.proto
    405. data_acquisition.proto
    406. @@ -403,6 +437,8 @@
    407. graph_nav/graph_nav.proto
    408. graph_nav/graph_nav_service.proto
    409. graph_nav/map.proto
    410. +
    411. graph_nav/map_processing.proto
    412. +
    413. graph_nav/map_processing_service.proto
    414. graph_nav/nav.proto
    415. graph_nav/recording.proto
    416. graph_nav/recording_service.proto
    417. @@ -410,6 +446,8 @@
    418. header.proto
    419. image.proto
    420. image_service.proto
    421. +
    422. ir_enable_disable.proto
    423. +
    424. ir_enable_disable_service.proto
    425. lease.proto
    426. lease_service.proto
    427. license.proto
    428. @@ -429,6 +467,7 @@
    429. mobility_command.proto
    430. network_compute_bridge.proto
    431. network_compute_bridge_service.proto
    432. +
    433. network_stats.proto
    434. parameter.proto
    435. payload.proto
    436. payload_estimation.proto
    437. @@ -446,6 +485,7 @@
    438. robot_state.proto
    439. robot_state_service.proto
    440. service_fault.proto
    441. +
    442. sparse_features.proto
    443. spot/door.proto
    444. spot/door_service.proto
    445. spot/robot_command.proto
    446. @@ -463,6 +503,7 @@
    447. spot_cam/service.proto
    448. spot_cam/streamquality.proto
    449. spot_cam/version.proto
    450. +
    451. stairs.proto
    452. synchronized_command.proto
    453. time_range.proto
    454. time_sync.proto
    455. diff --git a/docs/html/docs/python/fetch_tutorial/fetch1.html b/docs/html/docs/python/fetch_tutorial/fetch1.html index 5c07449a8..207e3f29a 100644 --- a/docs/html/docs/python/fetch_tutorial/fetch1.html +++ b/docs/html/docs/python/fetch_tutorial/fetch1.html @@ -8,7 +8,7 @@ - Tutorial: Playing Fetch with Spot — Spot 2.3.5 documentation + Tutorial: Playing Fetch with Spot — Spot 3.0.0 documentation @@ -42,7 +42,7 @@ - + @@ -69,7 +69,7 @@
      - 2.3.5 + 3.0.0
      @@ -102,6 +102,7 @@
    456. Geometry and Frames
    457. Robot services
    458. E-Stop
    459. +
    460. Lease
    461. Developing API Services
    462. Faults
    463. Autonomy services
    464. Choreography
    465. Spot Arm
    466. Robot Behavior and Commands Examples
    467. Arm Command Examples
    468. Logging Examples
    469. Autonomy and Missions Examples
    470. @@ -245,6 +271,7 @@
    471. Arm Surface Contact
    472. Async Tasks
    473. Auth
    474. +
    475. Auto Return
    476. BDDF
    477. BDDF Download
    478. Channel
    479. @@ -268,12 +295,14 @@
    480. Graph Nav
    481. Image
    482. Image Service Helpers
    483. +
    484. IR Enable/Disable
    485. Lease
    486. License
    487. Local Grid
    488. Log Annotation
    489. Math Helpers
    490. Manipulation API
    491. +
    492. Map Processing
    493. Network Compute Bridge
    494. Payload Registration
    495. Payload
    496. @@ -321,6 +350,7 @@
    497. GRPC Reader
    498. GRPC Service Reader
    499. GRPC Service Writer
    500. +
    501. Message Reader
    502. POD Series Reader
    503. POD Series Writer
    504. Protobuf Channel Reader
    505. @@ -344,6 +374,8 @@
    506. Choreography
    507. @@ -377,6 +409,8 @@
    508. arm_surface_contact_service.proto
    509. auth.proto
    510. auth_service.proto
    511. +
    512. auto_return/auto_return.proto
    513. +
    514. auto_return/auto_return_service.proto
    515. basic_command.proto
    516. bddf.proto
    517. data_acquisition.proto
    518. @@ -403,6 +437,8 @@
    519. graph_nav/graph_nav.proto
    520. graph_nav/graph_nav_service.proto
    521. graph_nav/map.proto
    522. +
    523. graph_nav/map_processing.proto
    524. +
    525. graph_nav/map_processing_service.proto
    526. graph_nav/nav.proto
    527. graph_nav/recording.proto
    528. graph_nav/recording_service.proto
    529. @@ -410,6 +446,8 @@
    530. header.proto
    531. image.proto
    532. image_service.proto
    533. +
    534. ir_enable_disable.proto
    535. +
    536. ir_enable_disable_service.proto
    537. lease.proto
    538. lease_service.proto
    539. license.proto
    540. @@ -429,6 +467,7 @@
    541. mobility_command.proto
    542. network_compute_bridge.proto
    543. network_compute_bridge_service.proto
    544. +
    545. network_stats.proto
    546. parameter.proto
    547. payload.proto
    548. payload_estimation.proto
    549. @@ -446,6 +485,7 @@
    550. robot_state.proto
    551. robot_state_service.proto
    552. service_fault.proto
    553. +
    554. sparse_features.proto
    555. spot/door.proto
    556. spot/door_service.proto
    557. spot/robot_command.proto
    558. @@ -463,6 +503,7 @@
    559. spot_cam/service.proto
    560. spot_cam/streamquality.proto
    561. spot_cam/version.proto
    562. +
    563. stairs.proto
    564. synchronized_command.proto
    565. time_range.proto
    566. time_sync.proto
    567. diff --git a/docs/html/docs/python/fetch_tutorial/fetch2.html b/docs/html/docs/python/fetch_tutorial/fetch2.html index 0d3a88f54..38116d386 100644 --- a/docs/html/docs/python/fetch_tutorial/fetch2.html +++ b/docs/html/docs/python/fetch_tutorial/fetch2.html @@ -8,7 +8,7 @@ - Fetch Part 2: Training the Model — Spot 2.3.5 documentation + Fetch Part 2: Training the Model — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
      - 2.3.5 + 3.0.0
      @@ -102,6 +102,7 @@
    568. Geometry and Frames
    569. Robot services
    570. E-Stop
    571. +
    572. Lease
    573. Developing API Services
    574. Faults
    575. Autonomy services
    576. Choreography
    577. Spot Arm
    578. Robot Behavior and Commands Examples
    579. Arm Command Examples
    580. Logging Examples
    581. Autonomy and Missions Examples
    582. @@ -245,6 +271,7 @@
    583. Arm Surface Contact
    584. Async Tasks
    585. Auth
    586. +
    587. Auto Return
    588. BDDF
    589. BDDF Download
    590. Channel
    591. @@ -268,12 +295,14 @@
    592. Graph Nav
    593. Image
    594. Image Service Helpers
    595. +
    596. IR Enable/Disable
    597. Lease
    598. License
    599. Local Grid
    600. Log Annotation
    601. Math Helpers
    602. Manipulation API
    603. +
    604. Map Processing
    605. Network Compute Bridge
    606. Payload Registration
    607. Payload
    608. @@ -321,6 +350,7 @@
    609. GRPC Reader
    610. GRPC Service Reader
    611. GRPC Service Writer
    612. +
    613. Message Reader
    614. POD Series Reader
    615. POD Series Writer
    616. Protobuf Channel Reader
    617. @@ -344,6 +374,8 @@
    618. Choreography
    619. @@ -377,6 +409,8 @@
    620. arm_surface_contact_service.proto
    621. auth.proto
    622. auth_service.proto
    623. +
    624. auto_return/auto_return.proto
    625. +
    626. auto_return/auto_return_service.proto
    627. basic_command.proto
    628. bddf.proto
    629. data_acquisition.proto
    630. @@ -403,6 +437,8 @@
    631. graph_nav/graph_nav.proto
    632. graph_nav/graph_nav_service.proto
    633. graph_nav/map.proto
    634. +
    635. graph_nav/map_processing.proto
    636. +
    637. graph_nav/map_processing_service.proto
    638. graph_nav/nav.proto
    639. graph_nav/recording.proto
    640. graph_nav/recording_service.proto
    641. @@ -410,6 +446,8 @@
    642. header.proto
    643. image.proto
    644. image_service.proto
    645. +
    646. ir_enable_disable.proto
    647. +
    648. ir_enable_disable_service.proto
    649. lease.proto
    650. lease_service.proto
    651. license.proto
    652. @@ -429,6 +467,7 @@
    653. mobility_command.proto
    654. network_compute_bridge.proto
    655. network_compute_bridge_service.proto
    656. +
    657. network_stats.proto
    658. parameter.proto
    659. payload.proto
    660. payload_estimation.proto
    661. @@ -446,6 +485,7 @@
    662. robot_state.proto
    663. robot_state_service.proto
    664. service_fault.proto
    665. +
    666. sparse_features.proto
    667. spot/door.proto
    668. spot/door_service.proto
    669. spot/robot_command.proto
    670. @@ -463,6 +503,7 @@
    671. spot_cam/service.proto
    672. spot_cam/streamquality.proto
    673. spot_cam/version.proto
    674. +
    675. stairs.proto
    676. synchronized_command.proto
    677. time_range.proto
    678. time_sync.proto
    679. diff --git a/docs/html/docs/python/fetch_tutorial/fetch3.html b/docs/html/docs/python/fetch_tutorial/fetch3.html index 9ef8922df..8485a4154 100644 --- a/docs/html/docs/python/fetch_tutorial/fetch3.html +++ b/docs/html/docs/python/fetch_tutorial/fetch3.html @@ -8,7 +8,7 @@ - Fetch Part 3: Evaluating the Model — Spot 2.3.5 documentation + Fetch Part 3: Evaluating the Model — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
      - 2.3.5 + 3.0.0
      @@ -102,6 +102,7 @@
    680. Geometry and Frames
    681. Robot services
    682. E-Stop
    683. +
    684. Lease
    685. Developing API Services
    686. Faults
    687. Autonomy services
    688. Choreography
    689. Spot Arm
    690. Robot Behavior and Commands Examples
    691. Arm Command Examples
    692. Logging Examples
    693. Autonomy and Missions Examples
    694. @@ -245,6 +271,7 @@
    695. Arm Surface Contact
    696. Async Tasks
    697. Auth
    698. +
    699. Auto Return
    700. BDDF
    701. BDDF Download
    702. Channel
    703. @@ -268,12 +295,14 @@
    704. Graph Nav
    705. Image
    706. Image Service Helpers
    707. +
    708. IR Enable/Disable
    709. Lease
    710. License
    711. Local Grid
    712. Log Annotation
    713. Math Helpers
    714. Manipulation API
    715. +
    716. Map Processing
    717. Network Compute Bridge
    718. Payload Registration
    719. Payload
    720. @@ -321,6 +350,7 @@
    721. GRPC Reader
    722. GRPC Service Reader
    723. GRPC Service Writer
    724. +
    725. Message Reader
    726. POD Series Reader
    727. POD Series Writer
    728. Protobuf Channel Reader
    729. @@ -344,6 +374,8 @@
    730. Choreography
    731. @@ -377,6 +409,8 @@
    732. arm_surface_contact_service.proto
    733. auth.proto
    734. auth_service.proto
    735. +
    736. auto_return/auto_return.proto
    737. +
    738. auto_return/auto_return_service.proto
    739. basic_command.proto
    740. bddf.proto
    741. data_acquisition.proto
    742. @@ -403,6 +437,8 @@
    743. graph_nav/graph_nav.proto
    744. graph_nav/graph_nav_service.proto
    745. graph_nav/map.proto
    746. +
    747. graph_nav/map_processing.proto
    748. +
    749. graph_nav/map_processing_service.proto
    750. graph_nav/nav.proto
    751. graph_nav/recording.proto
    752. graph_nav/recording_service.proto
    753. @@ -410,6 +446,8 @@
    754. header.proto
    755. image.proto
    756. image_service.proto
    757. +
    758. ir_enable_disable.proto
    759. +
    760. ir_enable_disable_service.proto
    761. lease.proto
    762. lease_service.proto
    763. license.proto
    764. @@ -429,6 +467,7 @@
    765. mobility_command.proto
    766. network_compute_bridge.proto
    767. network_compute_bridge_service.proto
    768. +
    769. network_stats.proto
    770. parameter.proto
    771. payload.proto
    772. payload_estimation.proto
    773. @@ -446,6 +485,7 @@
    774. robot_state.proto
    775. robot_state_service.proto
    776. service_fault.proto
    777. +
    778. sparse_features.proto
    779. spot/door.proto
    780. spot/door_service.proto
    781. spot/robot_command.proto
    782. @@ -463,6 +503,7 @@
    783. spot_cam/service.proto
    784. spot_cam/streamquality.proto
    785. spot_cam/version.proto
    786. +
    787. stairs.proto
    788. synchronized_command.proto
    789. time_range.proto
    790. time_sync.proto
    791. @@ -639,7 +680,7 @@

      Fetch Part 3: Evaluating the Model

      Import a bunch of packages and define a constant.


      @@ -1000,7 +1041,7 @@

      Fetch Part 3: Evaluating the Modelname type authority tokens -------------------------------------------------------------------------------------------------------------------------------- [...] -fetch-server bosdyn.api.NetworkComputeBridgeWorker auth.spot.robot user +fetch-server bosdyn.api.NetworkComputeBridgeWorker fetch-tutorial-worker.spot.robot user [...]

      Now we're ready to see results! Get Spot's tablet, connect to the robot, and stand it up. diff --git a/docs/html/docs/python/fetch_tutorial/fetch4.html b/docs/html/docs/python/fetch_tutorial/fetch4.html index 631e89d80..c32024804 100644 --- a/docs/html/docs/python/fetch_tutorial/fetch4.html +++ b/docs/html/docs/python/fetch_tutorial/fetch4.html @@ -8,7 +8,7 @@ - Fetch Part 4: Autonomous Pick Up — Spot 2.3.5 documentation + Fetch Part 4: Autonomous Pick Up — Spot 3.0.0 documentation @@ -69,7 +69,7 @@

      - 2.3.5 + 3.0.0
      @@ -102,6 +102,7 @@
    792. Geometry and Frames
    793. Robot services
    794. E-Stop
    795. +
    796. Lease
    797. Developing API Services
    798. Faults
    799. Autonomy services
    800. Choreography
    801. Spot Arm
    802. Robot Behavior and Commands Examples
    803. Arm Command Examples
    804. Logging Examples
    805. Autonomy and Missions Examples
    806. @@ -245,6 +271,7 @@
    807. Arm Surface Contact
    808. Async Tasks
    809. Auth
    810. +
    811. Auto Return
    812. BDDF
    813. BDDF Download
    814. Channel
    815. @@ -268,12 +295,14 @@
    816. Graph Nav
    817. Image
    818. Image Service Helpers
    819. +
    820. IR Enable/Disable
    821. Lease
    822. License
    823. Local Grid
    824. Log Annotation
    825. Math Helpers
    826. Manipulation API
    827. +
    828. Map Processing
    829. Network Compute Bridge
    830. Payload Registration
    831. Payload
    832. @@ -321,6 +350,7 @@
    833. GRPC Reader
    834. GRPC Service Reader
    835. GRPC Service Writer
    836. +
    837. Message Reader
    838. POD Series Reader
    839. POD Series Writer
    840. Protobuf Channel Reader
    841. @@ -344,6 +374,8 @@
    842. Choreography
    843. @@ -377,6 +409,8 @@
    844. arm_surface_contact_service.proto
    845. auth.proto
    846. auth_service.proto
    847. +
    848. auto_return/auto_return.proto
    849. +
    850. auto_return/auto_return_service.proto
    851. basic_command.proto
    852. bddf.proto
    853. data_acquisition.proto
    854. @@ -403,6 +437,8 @@
    855. graph_nav/graph_nav.proto
    856. graph_nav/graph_nav_service.proto
    857. graph_nav/map.proto
    858. +
    859. graph_nav/map_processing.proto
    860. +
    861. graph_nav/map_processing_service.proto
    862. graph_nav/nav.proto
    863. graph_nav/recording.proto
    864. graph_nav/recording_service.proto
    865. @@ -410,6 +446,8 @@
    866. header.proto
    867. image.proto
    868. image_service.proto
    869. +
    870. ir_enable_disable.proto
    871. +
    872. ir_enable_disable_service.proto
    873. lease.proto
    874. lease_service.proto
    875. license.proto
    876. @@ -429,6 +467,7 @@
    877. mobility_command.proto
    878. network_compute_bridge.proto
    879. network_compute_bridge_service.proto
    880. +
    881. network_stats.proto
    882. parameter.proto
    883. payload.proto
    884. payload_estimation.proto
    885. @@ -446,6 +485,7 @@
    886. robot_state.proto
    887. robot_state_service.proto
    888. service_fault.proto
    889. +
    890. sparse_features.proto
    891. spot/door.proto
    892. spot/door_service.proto
    893. spot/robot_command.proto
    894. @@ -463,6 +503,7 @@
    895. spot_cam/service.proto
    896. spot_cam/streamquality.proto
    897. spot_cam/version.proto
    898. +
    899. stairs.proto
    900. synchronized_command.proto
    901. time_range.proto
    902. time_sync.proto
    903. @@ -815,7 +856,7 @@

      Fetch Part 4: Autonomous Pick Up -
                  print('Found dogtoy...')
              # Got a dogtoy.  Request pick up.
      -
      -        # Stow the arm in case it is deployed
      -        stow_cmd = RobotCommandBuilder.arm_stow_command()
      -        command_client.robot_command(stow_cmd)
      -
      -        # NOTE: we'll enable this code in Part 5, when we understand it.
      -        # -------------------------
      -        # # Walk to the object.
      -        # walk_rt_vision, heading_rt_vision = compute_stand_location_and_yaw(
      -            # vision_tform_dogtoy, robot_state_client, distance_margin=1.0)
      -
      -        # move_cmd = RobotCommandBuilder.trajectory_command(
      -            # goal_x=walk_rt_vision[0],
      -            # goal_y=walk_rt_vision[1],
      -            # goal_heading=heading_rt_vision,
      -            # frame_name=frame_helpers.VISION_FRAME_NAME,
      -            # params=get_walking_params(0.5, 0.5))
      -        # end_time = 5.0
      -        # cmd_id = command_client.robot_command(command=move_cmd,
      -                                              # end_time_secs=time.time() +
      -                                              # end_time)
      -
      -        # # Wait until the robot reports that it is at the goal.
      -        # block_for_trajectory_cmd(command_client, cmd_id, timeout_sec=5, verbose=True)
      -        # -------------------------
      -
      -
      -

      -

      +

      +
                  print('Found dogtoy...')
      +
      +            # Got a dogtoy.  Request pick up.
      +
      +            # Stow the arm in case it is deployed
      +            stow_cmd = RobotCommandBuilder.arm_stow_command()
      +            command_client.robot_command(stow_cmd)
      +
      +            # NOTE: we'll enable this code in Part 5, when we understand it.
      +            # -------------------------
      +            # # Walk to the object.
      +            # walk_rt_vision, heading_rt_vision = compute_stand_location_and_yaw(
      +                # vision_tform_dogtoy, robot_state_client, distance_margin=1.0)
      +
      +            # move_cmd = RobotCommandBuilder.trajectory_command(
      +                # goal_x=walk_rt_vision[0],
      +                # goal_y=walk_rt_vision[1],
      +                # goal_heading=heading_rt_vision,
      +                # frame_name=frame_helpers.VISION_FRAME_NAME,
      +                # params=get_walking_params(0.5, 0.5))
      +            # end_time = 5.0
      +            # cmd_id = command_client.robot_command(command=move_cmd,
      +                                                  # end_time_secs=time.time() +
      +                                                  # end_time)
      +
      +            # # Wait until the robot reports that it is at the goal.
      +            # block_for_trajectory_cmd(command_client, cmd_id, timeout_sec=5, verbose=True)
      +            # -------------------------
      +
      +

      Right now, we aren't going to worry about walking longer distances, but in the next section we'll want to do that. Don't worry about this code now and we'll enable it in Part 5.


                  # The ML result is a bounding box.  Find the center.
      diff --git a/docs/html/docs/python/fetch_tutorial/fetch5.html b/docs/html/docs/python/fetch_tutorial/fetch5.html
      index df3231280..76a237bcc 100644
      --- a/docs/html/docs/python/fetch_tutorial/fetch5.html
      +++ b/docs/html/docs/python/fetch_tutorial/fetch5.html
      @@ -8,7 +8,7 @@
       
         
         
      -  Fetch Part 5: Detecting People and Playing Fetch — Spot 2.3.5 documentation
      +  Fetch Part 5: Detecting People and Playing Fetch — Spot 3.0.0 documentation
         
       
         
      @@ -69,7 +69,7 @@
                   
                   
                     
      - 2.3.5 + 3.0.0
      @@ -102,6 +102,7 @@
    904. Geometry and Frames
    905. Robot services
    906. E-Stop
    907. +
    908. Lease
    909. Developing API Services
    910. Faults
    911. Autonomy services
    912. Choreography
    913. Spot Arm
    914. Robot Behavior and Commands Examples
    915. Arm Command Examples
    916. Logging Examples
    917. Autonomy and Missions Examples
    918. @@ -245,6 +271,7 @@
    919. Arm Surface Contact
    920. Async Tasks
    921. Auth
    922. +
    923. Auto Return
    924. BDDF
    925. BDDF Download
    926. Channel
    927. @@ -268,12 +295,14 @@
    928. Graph Nav
    929. Image
    930. Image Service Helpers
    931. +
    932. IR Enable/Disable
    933. Lease
    934. License
    935. Local Grid
    936. Log Annotation
    937. Math Helpers
    938. Manipulation API
    939. +
    940. Map Processing
    941. Network Compute Bridge
    942. Payload Registration
    943. Payload
    944. @@ -321,6 +350,7 @@
    945. GRPC Reader
    946. GRPC Service Reader
    947. GRPC Service Writer
    948. +
    949. Message Reader
    950. POD Series Reader
    951. POD Series Writer
    952. Protobuf Channel Reader
    953. @@ -344,6 +374,8 @@
    954. Choreography
    955. @@ -377,6 +409,8 @@
    956. arm_surface_contact_service.proto
    957. auth.proto
    958. auth_service.proto
    959. +
    960. auto_return/auto_return.proto
    961. +
    962. auto_return/auto_return_service.proto
    963. basic_command.proto
    964. bddf.proto
    965. data_acquisition.proto
    966. @@ -403,6 +437,8 @@
    967. graph_nav/graph_nav.proto
    968. graph_nav/graph_nav_service.proto
    969. graph_nav/map.proto
    970. +
    971. graph_nav/map_processing.proto
    972. +
    973. graph_nav/map_processing_service.proto
    974. graph_nav/nav.proto
    975. graph_nav/recording.proto
    976. graph_nav/recording_service.proto
    977. @@ -410,6 +446,8 @@
    978. header.proto
    979. image.proto
    980. image_service.proto
    981. +
    982. ir_enable_disable.proto
    983. +
    984. ir_enable_disable_service.proto
    985. lease.proto
    986. lease_service.proto
    987. license.proto
    988. @@ -429,6 +467,7 @@
    989. mobility_command.proto
    990. network_compute_bridge.proto
    991. network_compute_bridge_service.proto
    992. +
    993. network_stats.proto
    994. parameter.proto
    995. payload.proto
    996. payload_estimation.proto
    997. @@ -446,6 +485,7 @@
    998. robot_state.proto
    999. robot_state_service.proto
    1000. service_fault.proto
    1001. +
    1002. sparse_features.proto
    1003. spot/door.proto
    1004. spot/door_service.proto
    1005. spot/robot_command.proto
    1006. @@ -463,6 +503,7 @@
    1007. spot_cam/service.proto
    1008. spot_cam/streamquality.proto
    1009. spot_cam/version.proto
    1010. +
    1011. stairs.proto
    1012. synchronized_command.proto
    1013. time_range.proto
    1014. time_sync.proto
    1015. diff --git a/docs/html/docs/python/fetch_tutorial/files/network_compute_server.py b/docs/html/docs/python/fetch_tutorial/files/network_compute_server.py index 751577c41..ecc3b41ea 100644 --- a/docs/html/docs/python/fetch_tutorial/files/network_compute_server.py +++ b/docs/html/docs/python/fetch_tutorial/files/network_compute_server.py @@ -30,7 +30,7 @@ from google.protobuf import wrappers_pb2 from object_detection.utils import label_map_util -kServiceAuthority = "auth.spot.robot" +kServiceAuthority = "fetch-tutorial-worker.spot.robot" class TensorFlowObjectDetectionModel: diff --git a/docs/html/docs/python/quickstart.html b/docs/html/docs/python/quickstart.html index 25aa3554a..0d78c40d4 100644 --- a/docs/html/docs/python/quickstart.html +++ b/docs/html/docs/python/quickstart.html @@ -8,7 +8,7 @@ - QuickStart — Spot 2.3.5 documentation + QuickStart — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
      - 2.3.5 + 3.0.0
      @@ -102,6 +102,7 @@
    1016. Geometry and Frames
    1017. Robot services
    1018. E-Stop
    1019. +
    1020. Lease
    1021. Developing API Services
    1022. Faults
    1023. Autonomy services
    1024. Choreography
    1025. Spot Arm
    1026. Robot Behavior and Commands Examples
    1027. Arm Command Examples
    1028. Logging Examples
    1029. Autonomy and Missions Examples
    1030. @@ -245,6 +271,7 @@
    1031. Arm Surface Contact
    1032. Async Tasks
    1033. Auth
    1034. +
    1035. Auto Return
    1036. BDDF
    1037. BDDF Download
    1038. Channel
    1039. @@ -268,12 +295,14 @@
    1040. Graph Nav
    1041. Image
    1042. Image Service Helpers
    1043. +
    1044. IR Enable/Disable
    1045. Lease
    1046. License
    1047. Local Grid
    1048. Log Annotation
    1049. Math Helpers
    1050. Manipulation API
    1051. +
    1052. Map Processing
    1053. Network Compute Bridge
    1054. Payload Registration
    1055. Payload
    1056. @@ -321,6 +350,7 @@
    1057. GRPC Reader
    1058. GRPC Service Reader
    1059. GRPC Service Writer
    1060. +
    1061. Message Reader
    1062. POD Series Reader
    1063. POD Series Writer
    1064. Protobuf Channel Reader
    1065. @@ -344,6 +374,8 @@
    1066. Choreography
    1067. @@ -377,6 +409,8 @@
    1068. arm_surface_contact_service.proto
    1069. auth.proto
    1070. auth_service.proto
    1071. +
    1072. auto_return/auto_return.proto
    1073. +
    1074. auto_return/auto_return_service.proto
    1075. basic_command.proto
    1076. bddf.proto
    1077. data_acquisition.proto
    1078. @@ -403,6 +437,8 @@
    1079. graph_nav/graph_nav.proto
    1080. graph_nav/graph_nav_service.proto
    1081. graph_nav/map.proto
    1082. +
    1083. graph_nav/map_processing.proto
    1084. +
    1085. graph_nav/map_processing_service.proto
    1086. graph_nav/nav.proto
    1087. graph_nav/recording.proto
    1088. graph_nav/recording_service.proto
    1089. @@ -410,6 +446,8 @@
    1090. header.proto
    1091. image.proto
    1092. image_service.proto
    1093. +
    1094. ir_enable_disable.proto
    1095. +
    1096. ir_enable_disable_service.proto
    1097. lease.proto
    1098. lease_service.proto
    1099. license.proto
    1100. @@ -429,6 +467,7 @@
    1101. mobility_command.proto
    1102. network_compute_bridge.proto
    1103. network_compute_bridge_service.proto
    1104. +
    1105. network_stats.proto
    1106. parameter.proto
    1107. payload.proto
    1108. payload_estimation.proto
    1109. @@ -446,6 +485,7 @@
    1110. robot_state.proto
    1111. robot_state_service.proto
    1112. service_fault.proto
    1113. +
    1114. sparse_features.proto
    1115. spot/door.proto
    1116. spot/door_service.proto
    1117. spot/robot_command.proto
    1118. @@ -463,6 +503,7 @@
    1119. spot_cam/service.proto
    1120. spot_cam/streamquality.proto
    1121. spot_cam/version.proto
    1122. +
    1123. stairs.proto
    1124. synchronized_command.proto
    1125. time_range.proto
    1126. time_sync.proto
    1127. @@ -565,7 +606,7 @@

      QuickStartSystem Requirements

    1128. Python Requirements

    1129. Pip Installation

    1130. -
    1131. Manage Multiple Python Environments with virtualenv

    1132. +
    1133. Manage Multiple Python Environments with virtualenv

    1134. Install Spot Python Packages

      @@ -660,8 +701,8 @@

      Manage multiple Python environments with virtualenv

      Install packages as needed, including Spot SDK.

    1135. $ python3 -m pip install virtualenv
      -$ python3 -m virtualenv my_spot_v2_0_env
      -$ source my_spot_v2_0_env/bin/activate
      +$ python3 -m virtualenv --python=/usr/bin/python3 my_spot_env
      +$ source my_spot_env/bin/activate
       $ (install packages including Spot SDK, code, edit, execute, etc.)
       
      @@ -669,20 +710,19 @@

      Manage multiple Python environments with virtualenv
      $ deactivate
       

      -

      Note: Please ensure the virtualenv was created using the expected version of python. If you see:

      -
      $ python -m virtualenv my_spot_v2_0_env
      -Running virtualenv with interpreter /usr/bin/python2
      -...
      -
      -
      -

      Then the wrong interpreter is being used. You can pass the interpreter as an additional argument, for example:

      -
      $ python -m virtualenv -p /usr/bin/python3 my_spot_v2_0_env
      +

      Note: Please ensure the virtualenv was created and uses the correct version of python by +starting python after activating the virtual environment.

      +
      (my_spot_env) user@yourcomputer:~/user $ python
      +Python 3.6.9 (default, Oct  8 2020, 12:12:24)
      +[GCC 8.4.0] on linux
      +Type "help", "copyright", "credits" or "license" for more information.
      +>>>
       

      Windows users:

      > py.exe -3 -m pip install virtualenv
      -> py.exe -3 -m virtualenv my_spot_v2_0_env
      -> .\my_spot_v2_0_env\Scripts\activate.bat
      +> py.exe -3 -m virtualenv my_spot_env
      +> .\my_spot_env\Scripts\activate.bat
       > (install packages including Spot SDK, code, edit, execute, etc.)
       
      @@ -698,14 +738,14 @@

      Install Spot Python packagesbosdyn-client, bosdyn-choreography-client and bosdyn-mission packages will also install bosdyn-api and bosdyn-core packages with the same version. The command above installs the latest version of the packages. To install a different version of the packages from PyPI, for -example 2.2.0, use the following command.

      -
      $ python3 -m pip install bosdyn-client==2.2.0 bosdyn-mission==2.2.0 bosdyn-choreography-client==2.2.0
      +example 3.0.0, use the following command.

      +
      $ python3 -m pip install bosdyn-client==3.0.0 bosdyn-mission==3.0.0 bosdyn-choreography-client==3.0.0
       

      Version incompatibility:

      If you see a version incompatibility error during pip install such as:

      ERROR: bosdyn-core <VERSION_STRING> has requirement bosdyn-api==<VERSION_STRING>, but you
      -have bosdyn-api 2.2.0 which is incompatible.
      +have bosdyn-api 3.0.0 which is incompatible.
       

      Try uninstalling the bosdyn packages (Note: unlike install, you will need to explicitly list all 4 packages) and then reinstalling:

      @@ -717,12 +757,12 @@

      Install Spot Python packages

      Make sure that the packages have been installed.

      $ python3 -m pip list --format=columns | grep bosdyn
      -bosdyn-api                    2.3.5
      -bosdyn-choreography-client    2.3.5
      -bosdyn-choreography-protos    2.3.5
      -bosdyn-client                 2.3.5
      -bosdyn-core                   2.3.5
      -bosdyn-mission                2.3.5
      +bosdyn-api                    3.0.0
      +bosdyn-choreography-client    3.0.0
      +bosdyn-choreography-protos    3.0.0
      +bosdyn-client                 3.0.0
      +bosdyn-core                   3.0.0
      +bosdyn-mission                3.0.0
       

      Windows users:

      @@ -782,7 +822,7 @@

      Get a user account on the robot

      Ping Spot

        -
      1. Power on Spot. Wait for the fans to turn off (and maybe 10-20 seconds after that)

      2. +
      3. Power on Spot by holding the power button down until the fans start. Wait for the fans to turn off (and maybe 10-20 seconds after that)

      4. Connect to Spot via wifi.

      5. Ping spot at 192.168.80.3

      @@ -800,11 +840,15 @@

      Request a Spot robot’s ID
      $ python3 -m bosdyn.client 192.168.80.3 id
       Could not contact robot with hostname "192.168.80.3"
       

      +
      $ python3 -m bosdyn.client 192.168.80.3 id
      +RetryableUnavailableError: _InactiveRpcError: gRPC service unavailable. Likely transient and can be resolved by retrying the request.
      +
      +

      The robot is not powered on or is unreachable. Go back and try to get your ping to work. You can also try the -v or --verbose to get more information to debug the issue.

    @@ -835,14 +879,13 @@

    Run Hello Spot - let’s see the robot move!
    2020-03-30 15:26:36,283 - ERROR - Robot is E-Stopped. Please use an external E-Stop client, such as
    +
    2021-03-30 15:26:36,283 - ERROR - Robot is E-Stopped. Please use an external E-Stop client, such as
     the E-Stop SDK example, to configure E-Stop.
     

    If you see the following error:

    $ python3 hello_spot.py --username usehername --password pazwierd 192.168.80.3
    -2020-04-03 15:10:28,189 - ERROR - Hello, Spot! threw an exception: bosdyn.api.GetAuthTokenResponse:
    -Provided username/password is invalid.
    +2021-04-03 15:10:28,189 - ERROR - Hello, Spot! threw an exception: InvalidLoginError()
     

    Your username or password is incorrect. Check your spelling and verify your credentials with your robot administrator.

    diff --git a/docs/html/docs/python/understanding_spot_programming.html b/docs/html/docs/python/understanding_spot_programming.html index 19a812a6e..f537e8105 100644 --- a/docs/html/docs/python/understanding_spot_programming.html +++ b/docs/html/docs/python/understanding_spot_programming.html @@ -8,7 +8,7 @@ - Understanding Spot Programming — Spot 2.3.5 documentation + Understanding Spot Programming — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -889,34 +930,78 @@

    Taking ownership of Spot (Leases)LeaseClient for the lease service and list the current leases:

    >>> lease_client = robot.ensure_client('lease')
     >>> lease_client.list_leases()
    -[resource: "body"
    +[resource: "all-leases"
    +lease {
    +  resource: "all-leases"
    +  epoch: "ZBEwqFkjbTznJRxU"
    +  sequence: 1
    +  client_names: "root"
    +}
    +lease_owner {
    +}
    +, resource: "body"
     lease {
       resource: "body"
    -  epoch: "IOSDMpfEqvdTHZGV"
    -  sequence: 0
    +  epoch: "ZBEwqFkjbTznJRxU"
    +  sequence: 1
    +  client_names: "root"
    +}
    +lease_owner {
    +}
    +, resource: "mobility"
    +lease {
    +  resource: "mobility"
    +  epoch: "ZBEwqFkjbTznJRxU"
    +  sequence: 1
    +  client_names: "root"
     }
     lease_owner {
     }
     ]
     
    -

    Lease-able resources are listed: currently the only resource supported is “body”, which covers all of the motors on Spot. Note that the lease_owner field is empty since no one has acquired the body lease.

    -

    Let’s acquire a lease and again list:

    -
    >>> lease_keep_alive = bosdyn.client.lease.LeaseKeepAlive(lease_client)
    ->>> lease = lease_client.acquire()
    +

    The lease-able resources are listed: “body”, “mobility”, (”full-arm”, “gripper”, “arm” for robots with an arm). The “body” resource covers all of the motors on Spot and for most use-cases it is sufficient to issue all commands just using the “body” lease since services on Spot will break apart the lease to use the minimal set of resources necessary.

    +

    NOTE: the lease_owner field is empty since no one has acquired the body lease.

    +

    When taking lease control of Spot, the lease should first be acquired, and then the “keepalive” should be created to retain ownership of the lease resource. +Let’s acquire a lease, create the keepalive, and again list:

    +
    >>> lease = lease_client.acquire()
    +>>> lease_keep_alive = bosdyn.client.lease.LeaseKeepAlive(lease_client)
     >>> lease_client.list_leases()
    -[resource: "body"
    +[resource: "all-leases"
    +lease {
    +  resource: "all-leases"
    +  epoch: "ZBEwqFkjbTznJRxU"
    +  sequence: 2
    +  client_names: "root"
    +}
    +lease_owner {
    +  client_name: "HelloSpotClientlaptop-kbrandes01:hello_spot.py-32049"
    +}
    +, resource: "body"
     lease {
       resource: "body"
    -  epoch: "IOSDMpfEqvdTHZGV"
    -  sequence: 1
    +  epoch: "ZBEwqFkjbTznJRxU"
    +  sequence: 2
    +  client_names: "root"
    +}
    +lease_owner {
    +  client_name: "HelloSpotClientlaptop-kbrandes01:hello_spot.py-32049"
    +}
    +, resource: "mobility"
    +lease {
    +  resource: "mobility"
    +  epoch: "ZBEwqFkjbTznJRxU"
    +  sequence: 2
    +  client_names: "root"
     }
     lease_owner {
    -  client_name: "understanding-spotbblank02:29516"
    +  client_name: "HelloSpotClientlaptop-kbrandes01:hello_spot.py-32049"
     }
     ]
     
    +

    After acquiring the “body” lease, you take ownership of the sub-resource “mobility”. For a robot with an arm, you will also take ownership of the sub-resources “full-arm”, “arm”, and “gripper” when acquiring the “body” lease.

    +

    NOTE: the lease keepalive object must remain in scope for the entire duration of the program that is using the lease for commands.

    Powering on the robot

    @@ -955,10 +1040,10 @@

    Commanding the robot>>> from bosdyn.geometry import EulerZXY >>> footprint_R_body = EulerZXY(yaw=0.4, roll=0.0, pitch=0.0) >>> from bosdyn.client.robot_command import RobotCommandBuilder ->>> cmd = RobotCommandBuilder.stand_command(footprint_R_body=footprint_R_body) +>>> cmd = RobotCommandBuilder.synchro_stand_command(footprint_R_body=footprint_R_body) >>> command_client.robot_command(cmd) # Command Spot to raise up. ->>> cmd = RobotCommandBuilder.stand_command(body_height=0.1) +>>> cmd = RobotCommandBuilder.synchro_stand_command(body_height=0.1) >>> command_client.robot_command(cmd) diff --git a/docs/html/docs/release_notes.html b/docs/html/docs/release_notes.html index fad6170aa..d5b54a788 100644 --- a/docs/html/docs/release_notes.html +++ b/docs/html/docs/release_notes.html @@ -8,7 +8,7 @@ - Spot Release Notes — Spot 2.3.5 documentation + Spot Release Notes — Spot 3.0.0 documentation @@ -68,7 +68,7 @@
    - 2.3.5 + 3.0.0
    @@ -101,6 +101,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -244,6 +270,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -267,12 +294,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -320,6 +349,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -343,6 +373,8 @@
  • Choreography
  • @@ -376,6 +408,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -402,6 +436,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -409,6 +445,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -428,6 +466,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -445,6 +484,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -462,6 +502,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -557,9 +598,247 @@

    Spot Release Notes

    -

    2.3.5

    +

    3.0.0

    New Features

    +
    +

    Graph Nav

    +

    Map Processing +The new map processing service provides two ways to process the data in a graph nav map:

    +
      +
    • adding new waypoints and edges to close loops and add connections in a map

    • +
    • optimizing “anchorings” of a map, which will generate optimized positions of waypoints in the world for display or navigation.

    • +
    +

    Navigate to Anchor +A new NaviagateToAnchor RPC can be used to command GraphNav to drive the robot to a specific place in an anchoring. GraphNav will find the waypoint that has the shortest path length from the robot’s current position but is still close to the goal.

    +

    See the Graph Nav documentation for more information.

    +
    +
    +

    Auto Return

    +

    Auto Return is a service which can be configured to take control of the robot in the event of a communication loss, and return it back along its recently traveled route to attempt to regain communications with its user. See the Auto Return documentation for more details.

    +
    +
    +

    Choreography

    +

    The Choreography API now provides ‘choreography logging’ which will capture timestamped data about the robot’s pose and joint state for either a user defined time period or for the duration of a dance. See the Choreography Service documentation for more details.

    +

    Users can now create animated moves using timestamped keyframes; these can be built through common animation software (like Autodesk Maya), handwritten, or constructed from choreography log data. The animated moves can be uploaded to the robot and used within dances like the base moves. See the Animations Overview documentation for more details.

    +
    +
    +

    Constrained manipulation

    +

    The constrained manipulation API provides the functionality to manipulate objects such as cranks, levers, cabinets and other similar objects with Spot. The API allows for the selection of a task type along with the desired task velocity to manipulate the object.

    +
    +
    +

    Pushbar Door Opening

    +

    API and tablet support for opening pushbar doors. The AutoPush API command takes a push point which the robot uses to detect the door and push it open at the specified location. See door.proto or arm_door.py for more details.

    +
    +
    +

    SpotCam

    +

    New SetPtzFocus and GetPtzFocus RPCs allow for control over the focus of the PTZ camera.

    +
    +
    +

    Payloads

    +

    Payload registration now supports mounting payloads to the wrist or gripper of the arm. The new MountFrameName enum contains the valid mounting locations.

    +

    A new UpdatePayloadAttached RPC allows for attaching and detaching payloads while the robot is operating.

    +
    +
    +

    Missions

    +

    Missions have a new STATUS_STOPPED state that can be triggered by the new StopMission rpc. This state differs from a paused mission in that it means that the mission is no longer running and cannot be resumed.

    +

    New mission node types:

    +
      +
    • BosdynNavigateRoute: Use GraphNav via NavigateRoute

    • +
    • BosdynRecordEvent: Record an API event in the data buffer.

    • +
    • SpotCamLed: Change the brightnesses of the LEDs on a SpotCam.

    • +
    • SpotCamResetAutofocus: Reset the autofocus on a SpotCam PTZ.

    • +
    • StoreMetadata: Attach metadata to some data stored by a DataAcquisition node.

    • +
    • RetainLease: Keep mission leases alive while the mission is running. This allows the mission to run a larger variety of missions without requiring the mission service’s client to keep the lease alive.

    • +
    • RestartWhenPaused: Restarts its child tree when a mission resumes, rather than resuming its child from the state it was in when it paused.

    • +
    +
    +
    +

    Enable and Disable IR Emitters

    +

    A new IREnableDisable service and request have been added. This request allows clients to enable/disable the robot’s IR light emitters in the body and hand sensors. This new service supports special situations where Spot’s emitters may interfere with a custom attached payload. Disabling the IR emission will cause a SystemFault to be raised and have a negative effect on mobility since the robot’s perception system is hindered.

    +
    +
    +
    +

    Bug fixes and improvements

    +
    +

    Graph Nav

    +

    For RPCs that can fail because the robot is impaired, the response message now includes a RobotImpairedState message providing details about the nature of the impairment.

    +

    NavigationFeedbackStatus now includes body_movement_status to make it simple to determine when the body has come to rest after completing navigation.

    +

    Directed exploration and alternate route finding can now be disabled using new fields in TravelParams.

    +

    RouteFollowingParams have been added to NavigateRouteRequest to specify the desired behavior in certain situations (not starting from the start of the route, resuming a route, and becoming blocked on a route).

    +

    New methods navigate_to_full() and navigate_route_full() has been added to the Graph Nav client which will return the full response instead of just the command id.

    +

    Additional statuses have been added to NavigateRouteResponse (STATUS_NO_PATH and STATUS_NOT_LOCALIZED_TO_MAP) to capture possible errors.

    +

    UploadGraph can fail with STATUS_INVALID_GRAPH when the specified graph is invalid, for example containing missing waypoints referenced by edges.

    +

    DownloadWaypointSnapshotRequest has an additional option to not include point cloud data. +The new has_remote_point_cloud_sensor field in WaypointSnapshot indicates that the point has point cloud data from a remote service.

    +

    Starting to record a new map can fail with the new status STATUS_TOO_FAR_FROM_EXISTING_MAP.

    +

    The RPC for creating a new waypoint can now be provided a list of world objects to include in that waypoint’s snapshot.

    +
    +
    +

    Missions

    +

    For very large missions, a new RPC has been added to the mission service to stream the mission to the robot in chunks, rather than as a single message. The chunks should still deserialize to the same LoadMissionRequest message when assembled.

    +

    When a mission node fails to compile, the resulting FailedNode message has a new string that lists the protobuf type of the node implementation.

    +
    +
    +

    Arm and Gripper Control

    +

    We have improved the feedback for ArmJointMoveCommand Requests to now include the status of the underlying trajectory planner and to return the planner’s solved trajectory, which is the trajectory the robot will execute.

    +

    The ManipulationFeedbackState contains extra enum values for additional placing states that the robot can be in during manipulation.

    +

    By default, the robot will assume all grasped items are not “carriable”. We have modified ApiGraspOverride to be able to override the carry state to one of CARRIABLE, NOT_CARRIABLE, or CARRIABLE_AND_STOWABLE.

    +

    If holding an item, the stowing behavior is:

    +
      +
    • NOT_CARRIABLE and CARRIABLE - The arm will not stow, instead it will stop

    • +
    • CARRIABLE_AND_STOWABLE - The arm will stow while continuing to grasp the item

    • +
    +

    In addition, the communication loss behavior of the arm when it is holding an item is also modified:

    +
      +
    • NOT_CARRIABLE - The arm will release the item and stow

    • +
    • CARRIABLE - The arm will not stow, instead entering stop

    • +
    • CARRIABLE_AND_STOWABLE - The arm will stow while continuing to grasp the item

    • +
    +
    +
    +

    Docking

    +

    The docking state includes additional “in-between” states, DOCK_STATUS_UNDOCKING and LINK_STATUS_DETECTING that indicate that a process is still on-going. +Additional errors have been added to DockingCommandResponse for particular ways that docking can fail (STATUS_ERROR_GRIPPER_HOLDING_ITEM, STATUS_ERROR_NOT_AVAILABLE, STATUS_ERROR_SYSTEM). +The feedback also has a new error status: STATUS_ERROR_NOT_AVAILABLE. +See the protobuf documentation for more details.

    +

    The docking python client include a new docking_command_full() call which returns the full response instead of only the command id. Additionally a new docking_command_feedback_full() returns the full feedback instead of only the status.

    +
    +
    +

    Network Compute Bridge

    +

    Models can now have a list of labels associated with them.

    +

    When the image is requested to be rotated, the rotation angle will be returned in the response.

    +
    +
    +

    Data Acquisition

    +

    DataAcquisitionCapabilities can now also report the plugin service name of the service that will be performing that acquisition.

    +

    The DataAcquisitionClient has a new acquire_data_from_request() that takes a full request proto, instead of building it internally.

    +

    The DataService client has been updated to have the correct service name.

    +
    +
    +

    Robot Commands

    +

    Power commands can now report a STATUS_OVERRIDDEN if the robot overrides the power command and disables motor power.

    +

    Power command responses and feedback will report system faults if a fault blocks the power command from succeeding.

    +

    LeaseUseResults have been added to messages for RobotCommand and ManipulationApi

    +

    New helpers safe_power_off_robot() and safe_power_cycle_robot() can completely power off the robot or power cycle the robot from any robot state.

    +

    Additional options have been added to ObstacleParams for tuning obstacle avoidance by turning off negative obstacle avoidance or body assist for avoiding foot obstacles.

    +
    +
    +

    Leases

    +

    Leases represent ownership over the robot. Leases have been updated to support ownership over only part of the robot, so that you can delegate control to different services, such as using Graph Nav to control robot mobility while simultaneously controlling the robot’s arm via a user-written script. Details are in the lease documentation, but in general users can just continue to use the “body” lease and everything will work as expected.

    +
    +
    +

    Other Changes

    +

    The image service will now report PixelFormat for JPEG image sources even though the pixel format can be determined from the JPEG header.

    +

    Spot Check has some additional failure statuses that it can report.

    +

    Events logged to the data buffer may now include a LogPreserveHint to tell the robot whether this event is worth preserving a log for.

    +

    RobotState now includes additional data about the terrain under each foot.

    +

    Additional properties have been added to world objects to assist in image processing.

    +

    An additional level of hierarchy has been added for transport-level errors to simplify most error cases. RpcError has two new subclasses: PersistentRpcError and RetryableRpcError. PersistentRpcError indicates an error such that attempting to retry the call will fail again. RetryableRpcError means that the call may succeed if retried.

    +

    Calling ensure_secure_channel() directly will now result in max message sizes not being correctly.

    +
    +
    +
    +

    Breaking Changes

    +

    Invalid RobotCommands will no longer result in STATUS_INVALID_REQUEST in the RobotCommandResponse message, but will instead use the CODE_INVALID_REQUEST error in the common header, like other RPCs do. In the python client library, this will still raise the same InvalidRequestError as before.

    +

    E-stops may not be unregistered from the estop service while the robot’s motors are powered. This prevents accidentally powering the robot off. A new STATUS_MOTORS_ON status will be returned in the response to indicate this error. To unregister an estop, first safely power off the robot.

    +

    RPCs to the AuthService and PayloadRegistration service are now rate-limited to 5 and 10 requests/second respectively. Requesting more than that will result in an HTTP 429 error, or raising the TooManyRequestsError if using the python client.

    +

    The “obstacle_distance” local grid inadvertently included some generated obstacles used only for foot-placement control. These grids no longer include those generated obstacle regions.

    +

    The python function bosdyn.client.lease.test_active_lease previously took an optional make_sublease argument. That has been replaced with an optional sublease_name argument so that if a sublease is desired, it gets created with a client name correctly.

    +

    The StraightStaircase message has been moved to bosdyn/api/stairs.proto so that it can be used in more places. This is compatible with existing serialized protobufs, but any code that is manually creating these messages will need to be updated.

    +
    +
    +

    Deprecations

    +
    +

    Robot Control

    +

    The enable_grated_floor field is superceded by the new grated_surfaces_mode which will auto-detect the need for grated surface handling.

    +

    The safe_power_off() helper has been replaced by the less ambiguous safe_power_off_motors() helper.

    +

    The docking_command_feedback() method of DockingClient incorrectly raised an exception if the docking command had encountered lease errors, and has been replaced by docking_command_feedback_full() which returns the full feedback response.

    +
    +
    +

    Graph Nav

    +

    For limiting the speed on an edge, use the vel_limit in mobility_params instead of the Edge annonation’s vel_limit.

    +
    +
    +

    Missions

    +

    Docking nodes should not use the child node anymore. If a mission needs to react to docking results, it should use the responses written into the blackboard by the docking node.

    +
    +
    +

    Payloads

    +

    The guid and secret fields on payload registration RPCs have been replaced with a standardized PayloadCredentials message.

    +
    +
    +

    Writing services

    +

    Many helpers for writing services and filling out responses have been moved from bosdyn.client.util to bosdyn.client.server_util.

    +

    bosdyn.mission.server_util.set_response_header() has been moved to bosdyn.client.server_util.set_response_header(), so that it can be used by any service, not only those depending on missions.

    +
    +
    +
    +

    Known Issues

    +

    When a network transport failure occurs, depending on the particular operating system and version of gRPC installed, the error from the python SDK may not always be the most specific error possible, such as UnknownDnsNameError. It may instead be raised as either a generic RpcError, or another generic failure type such as UnableToConnectToRobotError.

    +

    Spot CAM LED illumination levels are not currently recorded or played back in Autowalk missions.

    +

    If you write a custom data acquisition plugin or image service, do not change its DataAcquisitionCapability or ImageSource set once it is running and registered. New capabilities may not be detected, and old capabilities may still be listed as available in the Data Acquisition service. To change the capabilities of a service: unregister it from the directory, wait until its capabilities are no longer listed in the Data Acquisition service, and then re-register it. This waiting also applies to restarting a service if its capabilities will be different upon restart.

    +

    If you write a custom data acquisition plugin without using our helper class, its GetStatus() rpc is expected to complete immediately. If it takes too long to complete it can cause timeouts when requesting GetStatus() of the data acquisition service.

    +

    If you register a new service with the robot, calling robot.ensure_client() to create a client for that service may result in a UnregisteredServiceNameError.

    +
      +
    • Workaround: call robot.sync_with_directory() before robot.ensure_client()

    • +
    +

    SE2VelocityLimits require care. Correct usage of the SE2VelocityLimit message requires the user to fully fill out all the fields, setting unlimited values to a large number, say 1e6.

    +
    +
    +

    Sample Code

    +

    Animation Recorder (new) +Demonstrates recording motion made with the tablet and then playing it back with the choreographer service.

    +

    Arm Constrained Manipulation (new) +Demonstrates using constrained manipulation to turn a crank.

    +

    Auto Return (new) +Demonstrates setting up and triggering the Auto Return service.

    +

    Data Buffer (new) +Demonstrates adding several different kinds of data to the data buffer.

    +

    Get Depth Plus Visual Image (new) +Demonstrates how to use the depth_in_visual_frame image sources to visualize depth in a visual image.

    +

    Graph Nav Extract Point Cloud (new) +Demonstrates opening and parsing a GraphNav map and extracting a globally consistent point cloud from it.

    +

    Post-Docking Callbacks (new) +Builds docker images for mission callbacks that can upload data acquired during a mission.

    +

    Disable IR Emission (new) +Demonstrates enabling and disabling IR light emitters via the IREnableDisableService Client.

    +

    Arm Gaze (updated) +Updated to use block_until_arm_arrives() instead of a sleep()

    +

    BDDF Download (updated) +Now provides a Qt-based downloading UI.

    +

    Data Acquisition Service (updated) +Now provides a plugin for capturing data from the network compute bridge.

    +

    Frame Trajectory Command (updated) +Now takes arguments that specify how to move instead of performing fixed motions.

    +

    Graph Nav Command Line (updated) +Now can navigate to a position in an anchoring. The recording example provides options to automatically close loops in the map or optimize the map’s anchoring.

    +

    View Map (updated) +Now can display the map according to the anchoring.

    +

    Mission Recorder (updated) +New option to build a mission from the graph on the robot. +New command to automatically close loops in the graph. +Uses NavigateRoute to follow waypoints in order.

    +

    Network Compute Bridge (updated) +Now includes options to run and test a worker without needing a robot. Also includes files to build docker images for deployment.

    +

    Payloads (updated) +Now includes an example of how to attach and detach a payload.

    +

    Replay Mission (updated) +Now includes extra options for playing back Autowalk missions, as well as disabling directed exploration.

    +

    Ricoh Theta (updated) +Now includes a “live stream” option that provides a higher frame rate at the cost of lower-quality stitching. (Thanks Aaron Gokasian!)

    +

    Spot Cam (updated) +New options for getting and setting PTZ focus, audio capture channel, and audio capture gain. Also an option for enabling congestion control for the stream quality.

    +

    WASD (updated) +The Escape key can now be used to stop the robot.

    +

    Webcam Image Service (updated) +New resolution options allow for capturing from the webcam at different resolutions.

    +
    +
    +
    +

    2.3.5

    +
    +

    New Features

    Spot CAM

    Added InitializeLens rpc, which resets the PTZ autofocus without needing to power cycle the Spot CAM.

    @@ -570,61 +849,61 @@

    Data Bufferwrite_sync argument to the add_blob() and add_protobuf() methods of the DataBufferClient.

    -
    -

    Bug fixes and improvements

    +
    +

    Bug fixes and improvements

    When running with a Spot CAM+ (PTZ) running 2.3.5, the SetPowerStatus and CyclePower endpoints will now return an error if the PTZ is specified. These endpoints could previously cause the WebRTC feed to crash. These RPCs are still safe to use for other devices, but due to hardware limitations, resetting the PTZ power can possibly cause the Spot CAM to require a reboot to regain the WebRTC stream.

    RetryableUnavailableError was raised in more cases than it should have been, and it is now more selectively raised.

    The EstopKeepAlive expected users to monitor its status by popping entries out of its status_queue. If the user did not do so, the queue would continue to grow without bound. The queue size is now bounded and old unchecked entries will be thrown away. The queue size is specified with the max_status_queue_size argument to the constructor.

    -
    -

    Breaking Changes

    +
    +

    Breaking Changes

    As stated above, the behavior of SetPowerStatus and CyclePower endpoints have been changed for Spot CAM+ units when attempting to change the power status of the PTZ. They now return an error instead of modifying the power status of the PTZ. They remain functional for the Spot CAM+IR. Clients using these SDK endpoints to reset the autofocus on the PTZ are recommended to use InitializeLens instead. Other clients are encouraged to seek alternative options.

    -
    -

    Deprecations

    +
    +

    Deprecations

    The ResponseContext helper class has been moved to the bosdyn-client package (from the bosdyn-mission package), so that it can be used for gRPC logging in data acquisition plugin services in addition to the mission service. The new import location will be from bosdyn.client.server_util, and the original import location of bosdyn.mission.server_util has been deprecated.

    -
    -

    Known Issues

    +
    +

    Known Issues

    Same as 2.3.0

    -
    -

    2.3.4

    -
    -

    New Features

    +
    +

    2.3.4

    +
    +

    New Features

    Power Control

    New options have been added to the Power Service to allow for power cycling the robot and powering the payload ports or wifi radios on or off. Additional fields have been added to the robot state message to check the payload and wifi power states. Support has also been been added to the bosdyn.client command line interface for these commands.

    These new options will only work on some Enterprise Spot robots. Check the HardwareConfiguration message reported by a particular robot to see if it supports them.

    -
    -

    Bug fixes and improvements

    -

    Fixed an issue that could cause payload registration or directory registraion keep-alive threads to exit early in certain cases.

    +
    +

    Bug fixes and improvements

    +

    Fixed an issue that could cause payload registration or directory registration keep-alive threads to exit early in certain cases.

    Fixed a couple issues with the webcam example: updated the Dockerfile to create a smaller container specifically with python 3.7, added new optional argument to specify the video codec, and programmatically prevent substring arguments other than the --device-name argument to avoid accidental confusion with the docker container’s --device argument.

    -
    -

    Known Issues

    +
    +

    Known Issues

    Same as 2.3.0

    -
    -

    Sample Code

    +
    +

    Sample Code

    A new tutorial has been added to walk through using machine learning, the Network Compute Bridge, and Manipulation API to play fetch with Spot.

    -
    -

    2.3.3

    -
    -

    New Features

    -
    -

    Graph Nav

    +
    +

    2.3.3

    +
    +

    New Features

    +
    +

    Graph Nav

    The python Graph Nav client now allows setting an offset to the destination, for navigating to a position relative to the final waypoint instead of exactly matching the final waypoint position and orientation.

    -
    -

    Bug fixes and improvements

    +
    +

    Bug fixes and improvements

    Fixed issues where the data acquisition download helpers did not handle absolute paths on windows and did not clean filenames correctly. (Thanks David from Levatas!)

    Zip file names from the data download service no longer contain difficult characters.

    Fixed an issue where Graph Nav would sometimes report the robot was impaired on the first usage after restarting the robot.

    @@ -632,26 +911,26 @@

    Bug fixes and improvements -

    Breaking Changes

    +
    +

    Breaking Changes

    The estop service will now refuse to change configuration if the robot is already powered on, returning a status of STATUS_MOTORS_ON. This prevents accidentally cutting power while the robot is in operation.

    -
    -

    Known Issues

    +
    +

    Known Issues

    Same as 2.3.0

    -
    -

    Sample Code

    +
    +

    Sample Code

    Network Compute Bridge Updated to provide appropriate error messages from the workers.

    -
    -

    2.3.2

    -
    -

    New Features

    -
    -

    SpotCAM

    +
    +

    2.3.2

    +
    +

    New Features

    +
    +

    SpotCAM

    The SpotCAM API has been expanded to support more use cases for the SpotCAM+IR (thermal PTZ variant). This includes the following new endpoints:

    -
    -

    Known Issues

    +
    +

    Known Issues

    Same as 2.3.0

    -
    -

    Sample Code

    +
    +

    Sample Code

    Get image (updated) Support for the new pixel format of IR images.

    Ricoh Theta (updated) @@ -676,12 +955,12 @@

    Sample Code -

    2.3.0

    -
    -

    New Features

    -
    -

    Arm and Gripper Control

    +
    +

    2.3.0

    +
    +

    New Features

    +
    +

    Arm and Gripper Control

    One of the main features of the 2.3 release is control and support of Spot’s arm and gripper. Arm and gripper commands are included in the SynchronizedCommand message, and can be commanded in addition to mobility commands. The RobotCommandBuilder in the SDK provides many new helpers for building new arm and gripper commands. The synchro command builder functions now have an optional build_on_command argument, which is used to build a mobility/arm/gripper command onto an existing command, merging them correctly. The arm and gripper state are reported in the new manipulator_state field of the robot state. The Python SDK Robot class now has a has_arm() helper to determine if the robot has an arm or not. For more information about controlling the arm, see the arm documentation.

    @@ -692,8 +971,8 @@

    Arm and Gripper ControlDoors (beta) DoorService will automatically open and move through doors, once provided some information about handles and hinges.

    -
    -

    Network Compute Bridge

    +
    +

    Network Compute Bridge

    An interface for integrating real-time image processing and machine learning for identifying objects and aiding grasping. For more information, see the network compute bridge documentation.

    @@ -701,8 +980,8 @@

    Network Compute BridgePayload Estimation

    A new PayloadEstimationCommand is available to have Spot try to estimate the mass properties of a payload itself. After moving about to perform its estimation, the mass properties will be reported in the command feedback.

    -
    -

    SpotCAM

    +
    +

    SpotCAM

    Some SpotCAMs now include an IR camera. There are additional cameras and screens available for those versions, and a new pixel format PIXEL_FORMAT_GREYSCALE_U16 used to represent those IR images. There are additional rpcs used to set colormaps and overlays of the IR images for live display.

    Logpoint QUEUED status is now broken up further with the queue_status field, which differentiates between when the image has or has not been captured.

    @@ -710,25 +989,25 @@

    SpotCAMArm Support in Choreographer

    The new Choreographer executable now includes two new tracks, gripper and arm, and includes new dance moves which control the arm.

    -
    -

    Docking

    +
    +

    Docking

    A new PREP_POSE_UNDOCK command option can be used to undock a docked robot. It will return the new STATUS_ERROR_NOT_DOCKED if the robot was not already docked. When successful the status will be STATUS_AT_PREP_POSE.

    -
    -

    Graph Nav

    +
    +

    Graph Nav

    When localizing the robot using FIDUICIAL_INIT_SPECIFIC, if the target waypoint does not contain a good measurement of the desired fiducial, nearby waypoints may be used to infer the robot’s location. This behavior can be disabled with the new restrict_fiducial_detections_to_target_waypoint field to only use the waypoint’s own data.

    A new destination_waypoint_tform_body_goal is provided for the NavigateTo and NavigateRoute rpcs. This allows the user to specify a goal position that is offset from the destination waypoint, rather than exactly on the waypoint.

    A new command_id field can be specified on the NavigateTo and NavigateRoute rpcs. This is used to continue a previous command without needing to re-specify all the data. An important difference between specifying the command_id versus sending a new command is that if the robot has reported itself stuck, continuing a command will result in a STATUS_STUCK error, rather than trying again. A new STATUS_UNRECOGNIZED_COMMAND will be returned if the command_id does not match the currently executing command.

    The map representation now tracks the “source” of waypoints and edges. It is possible to override the cost of an edge used when planning paths and to disable the alternate route finding for a particular edge.

    -
    -

    Leases

    +
    +

    Leases

    Leases now include client names alongside the sequence number for help in debugging. If you are writing a custom client, make sure to append your client name any time that you create a sub-lease.

    ListLeases can optionally request to have the full lease information of the latest lease, rather than only the root lease information that was previously reported.

    -
    -

    Bug Fixes and Improvements

    +
    +

    Bug Fixes and Improvements

    In the seconds following modifications to the robot directory to the robot directories existing clients may experience a one time request failure. This failure is transient and can be resolved by retrying the request. This has been an expected failure case and a new error (RetryableUnavailableError) has been put in to better reflect the failure.

    SE3Pose and Quat math helpers have transform_vec3 members to simplify rotating vectors.

    There is a new get_self_ip() helper in common.py for determining the ip address your client will use to talk to the robot. This is useful for determining the correct registration information for a service, particularly on a machine with multiple network interfaces. This is also available via the python command line program python3 -m bosdyn.client <robot host> self-ip.

    @@ -738,21 +1017,21 @@

    Bug Fixes and Improvements

    The python SDK now depends on the Deprecated package, which is used to mark functions and classes that are deprecated and provide warnings, so that users are made aware that features that they are using may be removed in the future.

    -
    -

    Deprecations

    +
    +

    Deprecations

    The Deprecated package has been implemented across the SDK so that deprecated features will print out a warning when they are called. Documentation surrounding deprecated features has also been updated.

    -
    -

    Breaking Changes

    +
    +

    Breaking Changes

    Spot Check no longer computes the foot_height_results or leg_pair_results fields.

    All rpcs in the Python SDK have a default timeout of 30s. The global timeout can be changed by assigning a new value to bosdyn.client.common.DEFAULT_RPC_TIMEOUT, and individual rpc calls can still set their own timeout via a timeout=sec optional parameter at any call site.

    -
    -

    Known Issues

    +
    +

    Known Issues

    When a network transport failure occurs, depending on the particular operating system and version of gRPC installed, the error from the python SDK may not always be the most specific error possible, such as UnknownDnsNameError. It may instead be raised as either a generic RpcError, or another generic failure type such as UnableToConnectToRobotError.

    Spot CAM LED illumination levels are not currently recorded or played back in Autowalk missions.

    When capturing both a PTZ and Panoramic image in the same action, there may occasionally be two PTZ images captured along with the Panoramic image, rather than just one.

    -

    If you write a custom data acquisition plugin or image service, do not change its DataAcquisitionCapability or ImageSource set once it is running and registered. New capabilities may not be detected, and old capababilities may still be listed as available in the Data Acquisition service. To change the capabilities of a service: unregister it from the directory, wait until its capabilities are no longer listed in the Data Acquisition service, and then re-register it. This waiting also applies to restarting a service if its capabilities will be different upon restart.

    +

    If you write a custom data acquisition plugin or image service, do not change its DataAcquisitionCapability or ImageSource set once it is running and registered. New capabilities may not be detected, and old capabilities may still be listed as available in the Data Acquisition service. To change the capabilities of a service: unregister it from the directory, wait until its capabilities are no longer listed in the Data Acquisition service, and then re-register it. This waiting also applies to restarting a service if its capabilities will be different upon restart.

    If you write a custom data acquisition plugin without using our helper class, its GetStatus() rpc is expected to complete immediately. If it takes too long to complete it can cause timeouts when requesting GetStatus() of the data acquisition service.

    If you register a new service with the robot, calling robot.ensure_client() to create a client for that service may result in a UnregisteredServiceNameError.

    -
    -

    Sample Code

    +
    +

    Sample Code

    Arm and Mobility Command (new) Demonstrates how to issue both mobility and arm commands to the robot at the same time.

    Arm Door (new) @@ -817,10 +1096,10 @@

    Sample Code -

    2.2.0

    -
    -

    New Features

    +
    +

    2.2.0

    +
    +

    New Features

    Docking (license dependent)

    Automated docking at charging stations is out of beta and available for enterprise customers. There have been a few updates to the protos regarding status reporting and handling “prep” poses.

    @@ -840,8 +1119,8 @@

    Service and plugin developmentSE2TrajectoryCommand Feedback

    Added new status STATUS_NEAR_GOAL as well as a new BodyMovementStatus to help differentiate between different kinds of states the robot can be in at the end of its trajectory.

    -
    -

    Missions

    +
    +

    Missions

    The BosdynGraphNavLocalize node can now specify a full SetLocalizationRequest, rather than only localizing to the nearest fiducial.

    When comparing blackboard values, there is a new HandleStaleness option to specify what the node should do if the blackboard value is stale.

    @@ -854,8 +1133,8 @@

    Other Helper Functions**

    -
    -

    Bug fixes and improvements

    +
    +

    Bug fixes and improvements

    Data Acquisition Data Acquisition Service on robots is now robust to port number changes. The previous work around to this problem was to always specify the same port when starting/restarting a service. Now, the port argument for external api services can use the default, which will choose an available ephemeral port.

    Image Services @@ -871,16 +1150,16 @@

    Bug fixes and improvements -

    Deprecations

    +
    +

    Deprecations

    BDDF code has moved to the bosdyn-core package, so that it can be used separately from the client code. The new import location is bosdyn.bddf. The old import path via bosdyn.client.bddf is deprecated.

    Dependency Changes

    bosdyn-client now depends on the requests library for making http requests to download data acquisition results and bddf logs.

    -
    -

    Known issues

    +
    +

    Known issues

    When a network transport failure occurs, depending on the particular operating system and version of gRPC installed, the error from the python SDK may not always be the most specific error possible, such as UnknownDnsNameError. It may instead be raised as either a generic RpcError, or another generic failure type such as UnableToConnectToRobotError.

    Spot CAM LED illumination levels are not currently recorded or played back in Autowalk missions.

    When capturing both a PTZ and Panoramic image in the same action, there may occasionally be two PTZ images captured along with the Panoramic image, rather than just one.

    @@ -892,8 +1171,8 @@

    Known issuesSE2VelocityLimit message requires the user to fully fill out all the fields, setting unlimited values to a large number, say 1e6.

    -
    -

    Sample Code

    +
    +

    Sample Code

    Image service test program (new) A new tester program for image services is available to help with the development and debugging of new image service implementations.

    Docking (new) @@ -908,10 +1187,10 @@

    Sample Codetester_programs directory.

    -
    -

    2.1.0

    -
    -

    New Features

    +
    +

    2.1.0

    +
    +

    New Features

    Spot I/O: Data Acquisition

    This release features a new system for acquiring, storing, and retrieving sensor data. It comprises several new services and their associated clients.

    @@ -956,19 +1235,19 @@

    Data Logging

    Point cloud service definitions are provided for retrieving point cloud data from LiDAR sensors, such as from the EAP payload.

    -
    -

    Spot CAM

    +
    +

    Spot CAM

    Congestion control is now available for WebRTC streaming. External microphones supported, with control for selecting microphones and setting gain levels individually. Note: External microphone support only available on Spot CAM + IR.

    -
    -

    Graph Nav

    +
    +

    Graph Nav

    The localization data now includes a transform to a “seed” frame, providing a consistent global frame for use across multiple runs on the same map.

    Localization data can be requested relative to a particular waypoint, rather than only the waypoint that the robot is currently localized to.

    Additional control for determining whether the robot will navigate through areas with poor quality features.

    -
    -

    Missions

    +
    +

    Missions

    Additional mission nodes which support new functionality:

    +
  • IREnableDisableServiceClient (class in bosdyn.client.ir_enable_disable) +
  • is_alive() (bosdyn.client.directory_registration.DirectoryRegistrationKeepAlive method) - +
  • print_response() (in module bosdyn.client.common) +
  • +
  • process_anchoring() (bosdyn.client.map_processing.MapProcessingServiceClient method)
  • process_kwargs() (in module bosdyn.client.common) +
  • +
  • process_topology() (bosdyn.client.map_processing.MapProcessingServiceClient method)
  • proto() (bosdyn.client.async_tasks.AsyncPeriodicQuery property)
  • @@ -3432,6 +3897,8 @@

    P

  • proto_from_tuple() (in module bosdyn.mission.util)
  • ProtobufChannelReader (class in bosdyn.bddf.protobuf_channel_reader) +
  • +
  • ProtobufChannelReader.Iterator (class in bosdyn.bddf.protobuf_channel_reader)
  • ProtobufReader (class in bosdyn.bddf.protobuf_reader)
  • @@ -3473,6 +3940,10 @@

    R

  • (bosdyn.client.token_cache.TokenCacheFilesystem method)
  • +
  • read_and_find_animation_params() (in module bosdyn.choreography.client.animation_file_to_proto) +
  • +
  • read_animation_params() (in module bosdyn.choreography.client.animation_file_to_proto) +
  • read_checksum() (bosdyn.bddf.base_data_reader.BaseDataReader property) - +
  • series_identifier_to_hash() (bosdyn.bddf.file_indexer.FileIndexer static method)
  • -
  • series_index() (bosdyn.bddf.protobuf_reader.ProtobufReader method) +
  • series_index() (bosdyn.bddf.message_reader.MessageReader method)
  • -
  • series_index_to_descriptor() (bosdyn.bddf.protobuf_reader.ProtobufReader method) +
  • series_index_to_descriptor() (bosdyn.bddf.message_reader.MessageReader method)
  • series_spec() (bosdyn.bddf.pod_series_writer.PodSeriesWriter property) @@ -3805,6 +4294,8 @@

    S

  • (bosdyn.client.arm_surface_contact.ArmSurfaceContactClient attribute)
  • (bosdyn.client.auth.AuthClient attribute) +
  • +
  • (bosdyn.client.auto_return.AutoReturnClient attribute)
  • (bosdyn.client.data_acquisition.DataAcquisitionClient attribute)
  • @@ -3833,6 +4324,8 @@

    S

  • (bosdyn.client.graph_nav.GraphNavClient attribute)
  • (bosdyn.client.image.ImageClient attribute) +
  • +
  • (bosdyn.client.ir_enable_disable.IREnableDisableServiceClient attribute)
  • (bosdyn.client.lease.LeaseClient attribute)
  • @@ -3843,6 +4336,8 @@

    S

  • (bosdyn.client.log_annotation.LogAnnotationClient attribute)
  • (bosdyn.client.manipulation_api_client.ManipulationApiClient attribute) +
  • +
  • (bosdyn.client.map_processing.MapProcessingServiceClient attribute)
  • (bosdyn.client.network_compute_bridge_client.NetworkComputeBridgeClient attribute)
  • @@ -3904,6 +4399,14 @@

    S

  • ServiceFaultDoesNotExistError
  • ServiceUnavailableError +
  • +
  • set_audio_capture_channel() (bosdyn.client.spot_cam.audio.AudioClient method) +
  • +
  • set_audio_capture_channel_async() (bosdyn.client.spot_cam.audio.AudioClient method) +
  • +
  • set_audio_capture_gain() (bosdyn.client.spot_cam.audio.AudioClient method) +
  • +
  • set_audio_capture_gain_async() (bosdyn.client.spot_cam.audio.AudioClient method)
  • set_challenge() (bosdyn.client.estop.EstopEndpoint method)
  • @@ -3922,6 +4425,10 @@

    S

  • set_ir_colormap() (bosdyn.client.spot_cam.compositor.CompositorClient method)
  • set_ir_colormap_async() (bosdyn.client.spot_cam.compositor.CompositorClient method) +
  • +
  • set_ir_enabled() (bosdyn.client.ir_enable_disable.IREnableDisableServiceClient method) +
  • +
  • set_ir_enabled_async() (bosdyn.client.ir_enable_disable.IREnableDisableServiceClient method)
  • set_ir_meter_overlay() (bosdyn.client.spot_cam.compositor.CompositorClient method)
  • @@ -3946,10 +4453,12 @@

    S

  • set_passphrase_async() (bosdyn.client.spot_cam.media_log.MediaLogClient method)
  • set_power_status() (bosdyn.client.spot_cam.power.PowerClient method) +
  • +
  • set_power_status_async() (bosdyn.client.spot_cam.power.PowerClient method)
  • - - + @@ -4574,6 +5135,10 @@

    W

  • WaypointRegion (class in bosdyn.client.recording)
  • WorldObjectClient (class in bosdyn.client.world_object) +
  • +
  • wr0_handler() (in module bosdyn.choreography.client.animation_file_conversion_helpers) +
  • +
  • wr1_handler() (in module bosdyn.choreography.client.animation_file_conversion_helpers)
  • write() (bosdyn.bddf.pod_series_writer.PodSeriesWriter method) @@ -4585,10 +5150,12 @@

    W

  • (bosdyn.client.token_cache.TokenCacheFilesystem method)
  • -
  • write_data() (bosdyn.bddf.data_writer.DataWriter method) -
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -567,6 +608,8 @@

    Contentsarm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -593,6 +636,8 @@

    Contentsgraph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -600,6 +645,8 @@

    Contentsheader.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -619,6 +666,7 @@

    Contentsmobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -636,6 +684,7 @@

    Contentsrobot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -653,6 +702,7 @@

    Contentsspot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/protos/bosdyn/api/proto_reference.html b/docs/html/protos/bosdyn/api/proto_reference.html index 9083f1513..37e97263f 100644 --- a/docs/html/protos/bosdyn/api/proto_reference.html +++ b/docs/html/protos/bosdyn/api/proto_reference.html @@ -8,7 +8,7 @@ - arm_command.proto — Spot 2.3.5 documentation + arm_command.proto — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -846,6 +887,21 @@

    ArmJointMoveCommand.FeedbackArmJointMoveCommand.Feedback.Status Current status of the request. + +planner_status +ArmJointMoveCommand.Feedback.PlannerStatus +Current status of the trajectory planner. + + +planned_points +ArmJointTrajectoryPoint +Based on the user trajectory, the planned knot points that obey acceleration and velocity constraints. If these knot points don't match the requested knot points, consider increasing velocity/acceleration limits, and/or staying further away from joint position limits. In situations where we've modified you last point, we append a minimum time trajectory (that obeys the velocity and acceleration limits) from the planner's final point to the requested final point. This means that the length of planned_points may be one point larger than the requested. + + +time_to_goal +google.protobuf.Duration +Returns amount of time remaining until the joints are at the goal position. For multiple point trajectories, this is the time remaining to the final point. +

    @@ -1256,7 +1312,7 @@

    GazeCommand.RequestSE3Trajectory -Optional desired pose of the tool expressed in frame2. Will be constrained to 'look at' the target regardless of the requested orientation. +Optional, desired pose of the tool expressed in frame2. Will be constrained to 'look at' the target regardless of the requested orientation. frame2_name @@ -1266,7 +1322,7 @@

    GazeCommand.RequestSE3Pose -The tool pose relative to the parent link (wrist). Defaults to the gripper's hand frame position with the gripper camera's orientation. pos: [0.195570 0 0] rot: [0.9969173 0 -0.0784593 0] (-9 degrees about y) +The transformation of the tool pose relative to the parent link (wrist). If the field is left unset, the transform will default to: The position is wrist_tform_hand.position() [20 cm translation in wrist x]. The rotation is wrist_tform_hand_camera.rotation() [-9 degree pitch about wrist y]. target_trajectory_initial_velocity @@ -1400,6 +1456,40 @@

    ArmCartesianCommand.Request.AxisMode

    +

    +
    +

    ArmJointMoveCommand.Feedback.PlannerStatus

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameNumberDescription
    PLANNER_STATUS_UNKNOWN0PLANNER_STATUS_UNKNOWN should never be used. If used, an internal error has happened.
    PLANNER_STATUS_SUCCESS1A solution passing through the desired points and obeying the constraints was found.
    PLANNER_STATUS_MODIFIED2The planner had to modify the desired points in order to obey the constraints. For example, if you specify a 1 point trajectory, and tell it to get there in a really short amount of time, but haven't set a high allowable max velocity / acceleration, the planner will do its best to get as close as possible to the final point, but won't reach it. In situations where we've modified you last point, we append a minimum time trajectory (that obeys the velocity and acceleration limits) from the planner's final point to the requested final point.
    PLANNER_STATUS_FAILED3Failed to compute a valid trajectory, will go to first point instead. It is possible that our optimizer till fail to solve the problem instead of returning a sub-optimal solution. This is un-likely to occur.

    @@ -1416,7 +1506,7 @@

    ArmJointMoveCommand.Feedback.Status

    @@ -1964,18 +2054,15 @@

    AuthService

    -

    +

    -
    -

    basic_command.proto

    -

    -
    -

    ArmDragCommand

    -

    -
    -

    ArmDragCommand.Feedback

    +
    +

    auto_return/auto_return.proto

    +

    +
    +

    ConfigureRequest

    +

    Configure the AutoReturn system.

    @@ -1986,24 +2073,31 @@

    ArmDragCommand.Feedback

    - - - + + + + + + + + + + + + + + + + + + -
    statusArmDragCommand.Feedback.Statusheaderbosdyn.api.RequestHeaderCommon request header.
    leasesbosdyn.api.LeaseLeases that AutoReturn may use to accomplish its goals when AutoReturn automatically triggers. If left empty, AutoReturn will not automatically trigger.
    paramsParamsParameters to use when AutoReturn automatically triggers.
    clear_bufferboolForget any buffered locations the robot may be remembering. Defaults to false. Set to true if the robot has just crossed an area it should not traverse in AutoReturn.

    -
    -
    -

    ArmDragCommand.Request

    -

    -
    -
    -

    BatteryChangePoseCommand

    -

    Get the robot into a convenient pose for changing the battery

    -

    +

    -
    -

    BatteryChangePoseCommand.Feedback

    +
    +

    ConfigureResponse

    +

    Response to a configuration request.

    @@ -2014,15 +2108,26 @@

    BatteryChangePoseCommand.Feedbackbosdyn.api.ResponseHeader +

    + + - - + + + + + + + -
    Common response header.
    statusBatteryChangePoseCommand.Feedback.StatusConfigureResponse.StatusReturn status for the request.
    invalid_paramsParamsIf status is STATUS_INVALID_PARAMS, this contains the settings that were invalid.

    +

    -
    -

    BatteryChangePoseCommand.Request

    +
    +

    GetConfigurationRequest

    +

    Ask for the current configuration.

    @@ -2033,27 +2138,16 @@

    BatteryChangePoseCommand.RequestBatteryChangePoseCommand.Request.DirectionHint -

    + + + -
    headerbosdyn.api.RequestHeaderCommon request header.

    -
    -
    -

    FollowArmCommand

    -

    The base will move in response to the hand’s location, allow the arm to reach beyond -its current workspace. If the hand is moved forward, the body will begin walking -forward to keep the base at the desired offset from the hand.

    -

    -
    -
    -

    FollowArmCommand.Feedback

    -

    FollowArmCommand commands provide no feedback.

    -

    +

    -
    -

    FollowArmCommand.Request

    +
    +

    GetConfigurationResponse

    +

    Response to a “get configuration” request.

    @@ -2064,46 +2158,26 @@

    FollowArmCommand.RequestVec3 -

    + + + - + - + + + + + + -
    Optional body offset from the hand. For example, to have the body 0.75 meters behind the hand, use (0.75, 0, 0)headerbosdyn.api.ResponseHeaderCommon response header.
    disable_walkingenabled boolOptional. If true, the body will be restricted to body orientation offsets only.A simple yes/no, will AutoReturn automatically trigger.
    requestConfigureRequestThe most recent successful ConfigureRequest. Will be empty if service has not successfully been configured.

    -
    -
    -

    FreezeCommand

    -

    Freeze all joints at their current positions (no balancing control).

    -

    -
    -
    -

    FreezeCommand.Feedback

    -

    Freeze command provides no feedback.

    -

    +

    -
    -

    FreezeCommand.Request

    -

    Freeze command request takes no additional arguments.

    -

    -
    -
    -

    RobotCommandFeedbackStatus

    -

    -
    -
    -

    SE2TrajectoryCommand

    -

    Move along a trajectory in 2D space.

    -

    -
    -
    -

    SE2TrajectoryCommand.Feedback

    -

    The SE2TrajectoryCommand will provide feedback on whether or not the robot has reached the -final point of the trajectory.

    +
    +

    Params

    +

    Parameters to AutoReturn actions.

    @@ -2114,20 +2188,23 @@

    SE2TrajectoryCommand.FeedbackSE2TrajectoryCommand.Feedback.Status -

    + + + - - - + + + -
    Current status of the command.mobility_paramsgoogle.protobuf.AnyRobot-specific mobility parameters to use. For example, see bosdyn.api.spot.MobilityParams.
    body_movement_statusSE2TrajectoryCommand.Feedback.BodyMovementStatusCurrent status of how the body is movingmax_displacementfloatAllow AutoReturn to move the robot this far in the XY plane from the location where AutoReturn started. Travel along the Z axis (which is gravity-aligned) does not count. Must be > 0.

    +

    meters | +| max_duration | google.protobuf.Duration | Optionally specify the maximum amount of time AutoReturn can take. If this duration is exceeded, AutoReturn will stop trying to move the robot. Omit to try indefinitely. Robot may become stuck and never take other comms loss actions! |

    +

    -
    -

    SE2TrajectoryCommand.Request

    +
    +

    StartRequest

    +

    Start AutoReturn behavior now.

    @@ -2138,35 +2215,15 @@

    SE2TrajectoryCommand.Requestgoogle.protobuf.Timestamp -

    - - - - - - - - - - + + + -
    The timestamp (in robot time) by which a command must finish executing. This is a required field and used to prevent runaway commands.
    se2_frame_namestringThe name of the frame that trajectory is relative to. The trajectory must be expressed in a gravity aligned frame, so either "vision", "odom", or "body". Any other provided se2_frame_name will be rejected and the trajectory command will not be exectuted.
    trajectorySE2TrajectoryThe trajectory that the robot should follow, expressed in the frame identified by se2_frame_name.headerbosdyn.api.RequestHeaderCommon request header.

    -
    -
    -

    SE2VelocityCommand

    -

    Move the robot at a specific SE2 velocity for a fixed amount of time.

    -

    -
    -
    -

    SE2VelocityCommand.Feedback

    -

    Planar velocity commands provide no feedback.

    -

    +

    -
    -

    SE2VelocityCommand.Request

    +
    +

    StartResponse

    @@ -2177,83 +2234,89 @@

    SE2VelocityCommand.Requestgoogle.protobuf.Timestamp -

    + + + + +
    The timestamp (in robot time) by which a command must finish executing. This is a required field and used to prevent runaway commands.headerbosdyn.api.ResponseHeaderCommon response header.

    +
    +
    +

    ConfigureResponse.Status

    + + - - - + + + + + - - - + + + - - - + + + + + + + + -
    se2_frame_namestringThe name of the frame that velocity and slew_rate_limit are relative to. The trajectory must be expressed in a gravity aligned frame, so either "vision", "odom", or "flat_body". Any other provided se2_frame_name will be rejected and the velocity command will not be executed.NameNumberDescription
    velocitySE2VelocityDesired planar velocity of the robot body relative to se2_frame_name.STATUS_UNKNOWN0
    slew_rate_limitSE2VelocityIf set, limits how quickly velocity can change relative to se2_frame_name. Otherwise, robot may decide to limit velocities using default settings. These values should be non-negative.STATUS_OK1
    STATUS_INVALID_PARAMS2

    +

    -
    -

    SafePowerOffCommand

    -

    Get robot into a position where it is safe to power down, then power down. If the robot has -fallen, it will power down directly. If the robot is not in a safe position, it will get to a -safe position before powering down. The robot will not power down until it is in a safe state.

    -

    -
    -

    SafePowerOffCommand.Feedback

    -

    The SafePowerOff will provide feedback on whether or not it has succeeded in powering off -the robot yet.

    +
    +

    auto_return/auto_return_service.proto

    +

    +
    +

    AutoReturnService

    - - + + + - - - + + + + + + + + + + + + + + + + -
    FieldTypeMethod NameRequest TypeResponse Type Description
    statusSafePowerOffCommand.Feedback.StatusCurrent status of the command.ConfigureConfigureRequestConfigureResponseConfigure the service.
    GetConfigurationGetConfigurationRequestGetConfigurationResponseGet the current configuration.
    StartStartRequestStartResponseStart AutoReturn now.

    -
    -
    -

    SafePowerOffCommand.Request

    -

    SafePowerOff command request takes no additional arguments.

    -

    +

    -
    -

    SelfRightCommand

    -

    Move the robot into a “ready” position from which it can sit or stand up.

    -

    -
    -

    SelfRightCommand.Feedback

    -

    SelfRight command request provides no feedback.

    -

    -
    -
    -

    SelfRightCommand.Request

    -

    SelfRight command request takes no additional arguments.

    -

    -
    -
    -

    SitCommand

    -

    Sit the robot down in its current position.

    -

    +
    +

    basic_command.proto

    +

    +
    +

    ArmDragCommand

    +

    -
    -

    SitCommand.Feedback

    +
    +

    ArmDragCommand.Feedback

    @@ -2265,19 +2328,23 @@

    SitCommand.FeedbackSitCommand.Feedback.Status -

    + + -
    Current status of the command.ArmDragCommand.Feedback.Status

    +

    -
    -

    SitCommand.Request

    -

    Sit command request takes no additional arguments.

    -

    +
    +

    ArmDragCommand.Request

    +

    -
    -

    Stance

    +
    +

    BatteryChangePoseCommand

    +

    Get the robot into a convenient pose for changing the battery

    +

    +
    +
    +

    BatteryChangePoseCommand.Feedback

    @@ -2288,25 +2355,15 @@

    Stancestring -

    - - - - - - - - - - + + + -
    The frame name which the desired foot_positions are described in.
    foot_positionsStance.FootPositionsEntryMap of foot name to its x,y location in specified frame. Required positions for spot: "fl", "fr", "hl", "hr".
    accuracyfloatRequired foot positional accuracy in meters Advised = 0.05 ( 5cm) Minimum = 0.02 ( 2cm) Maximum = 0.10 (10cm)statusBatteryChangePoseCommand.Feedback.Status

    +

    -
    -

    Stance.FootPositionsEntry

    +
    +

    BatteryChangePoseCommand.Request

    @@ -2317,26 +2374,19 @@

    Stance.FootPositionsEntrystring -

    - - - - + + -
    valueVec2direction_hintBatteryChangePoseCommand.Request.DirectionHint

    +

    -
    -

    StanceCommand

    -

    Precise foot placement -This can be used to reposition the robots feet in place.

    -

    +
    +

    ConstrainedManipulationCommand

    +

    -
    -

    StanceCommand.Feedback

    +
    +

    ConstrainedManipulationCommand.Feedback

    @@ -2348,14 +2398,19 @@

    StanceCommand.Feedback

    - + + + + + + -
    statusStanceCommand.Feedback.StatusConstrainedManipulationCommand.Feedback.Status
    desired_wrench_odom_frameWrenchDesired wrench in odom world frame, applied at hand frame origin

    +

    -
    -

    StanceCommand.Request

    +
    +

    ConstrainedManipulationCommand.Request

    @@ -2366,27 +2421,62 @@

    StanceCommand.Request

    - - - + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -
    end_timegoogle.protobuf.TimestampThe timestamp (in robot time) by which a command must finish executing. / This is a required field and used to prevent runaway commands.frame_namestringFrame that the initial wrench will be expressed in
    stanceStanceinit_wrench_direction_in_frame_nameWrenchDirection of the initial wrench to be applied Depending on the task, either the force vector or the torque vector are required to be specified. The required vector should not have a magnitude of zero and will be normalized to 1. For tasks that require the force vector, the torque vector can still be specified as a non-zero vector if it is a good guess of the axis of rotation of the task. (for e.g. TASK_TYPE_SE3_ROTATIONAL_TORQUE task types.) Note that if both vectors are non-zero, they have to be perpendicular. Once the constrained manipulation system estimates the constraint, the init_wrench_direction and frame_name will no longer be used.
    tangential_speeddoubleRecommended values are in the range of [-4, 4] m/s
    rotational_speeddoubleRecommended values are in the range of [-4, 4] rad/s
    force_limitgoogle.protobuf.DoubleValueThe limit on the force that is applied on any translation direction Value must be positive If unspecified, a default value of 40 N will be used.
    torque_limitgoogle.protobuf.DoubleValueThe limit on the torque that is applied on any rotational direction Value must be positive If unspecified, a default value of 4 Nm will be used.
    task_typeConstrainedManipulationCommand.Request.TaskType
    end_timegoogle.protobuf.TimestampThe timestamp (in robot time) by which a command must finish executing. This is a required field and used to prevent runaway commands.

    +

    -
    -

    StandCommand

    -

    The stand the robot up in its current position.

    -

    +
    +

    FollowArmCommand

    +

    The base will move in response to the hand’s location, allow the arm to reach beyond +its current workspace. If the hand is moved forward, the body will begin walking +forward to keep the base at the desired offset from the hand.

    +

    -
    -

    StandCommand.Feedback

    -

    The StandCommand will provide feedback on whether or not the robot has finished -standing up.

    +
    +

    FollowArmCommand.Feedback

    +

    FollowArmCommand commands provide no feedback.

    +

    +
    +
    +

    FollowArmCommand.Request

    @@ -2397,331 +2487,368 @@

    StandCommand.Feedback

    - - - + + + + + + + + -
    statusStandCommand.Feedback.StatusCurrent status of the command.body_offset_from_handVec3Optional body offset from the hand. For example, to have the body 0.75 meters behind the hand, use (0.75, 0, 0)
    disable_walkingboolOptional. If true, the body will be restricted to body orientation offsets only.

    +

    -
    -

    StandCommand.Request

    -

    Stand command request takes no additional arguments.

    -

    +
    +

    FreezeCommand

    +

    Freeze all joints at their current positions (no balancing control).

    +

    -
    -

    StopCommand

    -

    Stop the robot in place with minimal motion.

    -

    +
    +

    FreezeCommand.Feedback

    +

    Freeze command provides no feedback.

    +

    -
    -

    StopCommand.Feedback

    -

    Stop command provides no feedback.

    -

    +
    +

    FreezeCommand.Request

    +

    Freeze command request takes no additional arguments.

    +

    -
    -

    StopCommand.Request

    -

    Stop command request takes no additional arguments.

    -

    +
    +

    RobotCommandFeedbackStatus

    +

    -
    -

    ArmDragCommand.Feedback.Status

    +
    +

    SE2TrajectoryCommand

    +

    Move along a trajectory in 2D space.

    +

    +
    +
    +

    SE2TrajectoryCommand.Feedback

    +

    The SE2TrajectoryCommand will provide feedback on whether or not the robot has reached the +final point of the trajectory.

    - - + + - - - - - - - - - - - - - + + + - - - + + + -
    NameNumberFieldType Description
    STATUS_UNKNOWN0STATUS_UNKNOWN should never be used. If used, an internal error has happened.
    STATUS_DRAGGING1Robot is dragging.
    STATUS_GRASP_FAILED2Robot is not dragging because grasp failed. This could be due to a lost grasp during a drag, or because the gripper isn't in a good position at the time of request. You'll have to reposition or regrasp and then send a new drag request to overcome this type of error. Note: When requesting drag, make sure the gripper is positioned in front of the robot (not to the side of or above the robot body).statusSE2TrajectoryCommand.Feedback.StatusCurrent status of the command.
    STATUS_OTHER_FAILURE3Robot is not dragging for another reason. This might be because the gripper isn't holding an item. You can continue dragging once you resolve this type of error (i.e. by sending an ApiGraspOverride request). Note: When requesting drag, be sure to that the robot knows it's holding something (or use APIGraspOverride to OVERRIDE_HOLDING).body_movement_statusSE2TrajectoryCommand.Feedback.BodyMovementStatusCurrent status of how the body is moving

    +

    -
    -

    BatteryChangePoseCommand.Feedback.Status

    +
    +

    SE2TrajectoryCommand.Request

    - - + + - - - - - - - - + + + - - - + + + - - - + + + -
    NameNumberFieldType Description
    STATUS_UNKNOWN0
    STATUS_COMPLETED1Robot is finished rollingend_timegoogle.protobuf.TimestampThe timestamp (in robot time) by which a command must finish executing. This is a required field and used to prevent runaway commands.
    STATUS_IN_PROGRESS2Robot still in process of rolling overse2_frame_namestringThe name of the frame that trajectory is relative to. The trajectory must be expressed in a gravity aligned frame, so either "vision", "odom", or "body". Any other provided se2_frame_name will be rejected and the trajectory command will not be exectuted.
    STATUS_FAILED3Robot has failed to roll onto its sidetrajectorySE2TrajectoryThe trajectory that the robot should follow, expressed in the frame identified by se2_frame_name.

    +

    -
    -

    BatteryChangePoseCommand.Request.DirectionHint

    +
    +

    SE2VelocityCommand

    +

    Move the robot at a specific SE2 velocity for a fixed amount of time.

    +

    +
    +
    +

    SE2VelocityCommand.Feedback

    +

    Planar velocity commands provide no feedback.

    +

    +
    +
    +

    SE2VelocityCommand.Request

    - - + + - - - + + + - - - + + + - - - + + + + + + + + -
    NameNumberFieldType Description
    HINT_UNKNOWN0Unknown direction, just hold stillend_timegoogle.protobuf.TimestampThe timestamp (in robot time) by which a command must finish executing. This is a required field and used to prevent runaway commands.
    HINT_RIGHT1Roll over right (right feet end up under the robot)se2_frame_namestringThe name of the frame that velocity and slew_rate_limit are relative to. The trajectory must be expressed in a gravity aligned frame, so either "vision", "odom", or "flat_body". Any other provided se2_frame_name will be rejected and the velocity command will not be executed.
    HINT_LEFT2Roll over left (left feet end up under the robot)velocitySE2VelocityDesired planar velocity of the robot body relative to se2_frame_name.
    slew_rate_limitSE2VelocityIf set, limits how quickly velocity can change relative to se2_frame_name. Otherwise, robot may decide to limit velocities using default settings. These values should be non-negative.

    +

    -
    -

    RobotCommandFeedbackStatus.Status

    +
    +

    SafePowerOffCommand

    +

    Get robot into a position where it is safe to power down, then power down. If the robot has +fallen, it will power down directly. If the robot is not in a safe position, it will get to a +safe position before powering down. The robot will not power down until it is in a safe state.

    +

    +
    +
    +

    SafePowerOffCommand.Feedback

    +

    The SafePowerOff will provide feedback on whether or not it has succeeded in powering off +the robot yet.

    - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + -
    NameNumberFieldType Description
    STATUS_UNKNOWN0Behavior execution is in an unknown / unexpected state.
    STATUS_PROCESSING1The robot is actively working on the command
    STATUS_COMMAND_OVERRIDDEN2The command was replaced by a new command
    STATUS_COMMAND_TIMED_OUT3The command expired
    STATUS_ROBOT_FROZEN4The robot is in an unsafe state, and will only respond to known safe commands.
    STATUS_INCOMPATIBLE_HARDWARE5The request cannot be executed because the required hardware is missing. / For example, an armless robot receiving a synchronized command with an arm_command / request will return this value in the arm_command_feedback status.statusSafePowerOffCommand.Feedback.StatusCurrent status of the command.

    +

    -
    -

    SE2TrajectoryCommand.Feedback.BodyMovementStatus

    +
    +

    SafePowerOffCommand.Request

    +

    SafePowerOff command request takes no additional arguments.

    +

    +
    +
    +

    SelfRightCommand

    +

    Move the robot into a “ready” position from which it can sit or stand up.

    +

    +
    +
    +

    SelfRightCommand.Feedback

    +

    SelfRight command request provides no feedback.

    +

    +
    +
    +

    SelfRightCommand.Request

    +

    SelfRight command request takes no additional arguments.

    +

    +
    +
    +

    SitCommand

    +

    Sit the robot down in its current position.

    +

    +
    +
    +

    SitCommand.Feedback

    - - + + - - - - - - - - - - - - - + + + -
    NameNumberFieldType Description
    BODY_STATUS_UNKNOWN0STATUS_UNKNOWN should never be used. If used, an internal error has happened.
    BODY_STATUS_MOVING1The robot body is not settled at the goal.
    BODY_STATUS_SETTLED2The robot is at the goal and the body has stopped moving.statusSitCommand.Feedback.StatusCurrent status of the command.

    +

    -
    -

    SE2TrajectoryCommand.Feedback.Status

    +
    +

    SitCommand.Request

    +

    Sit command request takes no additional arguments.

    +

    +
    +
    +

    Stance

    - - + + - - - - - - - - + + + - - - + + + - - - + + + -
    NameNumberFieldType Description
    STATUS_UNKNOWN0STATUS_UNKNOWN should never be used. If used, an internal error has happened.
    STATUS_AT_GOAL1The robot has arrived and is standing at the goal.se2_frame_namestringThe frame name which the desired foot_positions are described in.
    STATUS_NEAR_GOAL3The robot has arrived at the goal and is doing final positioning.foot_positionsStance.FootPositionsEntryMap of foot name to its x,y location in specified frame. Required positions for spot: "fl", "fr", "hl", "hr".
    STATUS_GOING_TO_GOAL2The robot is attempting to go to a goal.accuracyfloatRequired foot positional accuracy in meters Advised = 0.05 ( 5cm) Minimum = 0.02 ( 2cm) Maximum = 0.10 (10cm)

    +

    -
    -

    SafePowerOffCommand.Feedback.Status

    +
    +

    Stance.FootPositionsEntry

    - - + + - - - - - - - - + + + - - - + + + -
    NameNumberFieldType Description
    STATUS_UNKNOWN0STATUS_UNKNOWN should never be used. If used, an internal error has happened.
    STATUS_POWERED_OFF1Robot has powered off.keystring
    STATUS_IN_PROGRESS2Robot is trying to safely power off.valueVec2

    +

    -
    -

    SitCommand.Feedback.Status

    +
    +

    StanceCommand

    +

    Precise foot placement +This can be used to reposition the robots feet in place.

    +

    +
    +
    +

    StanceCommand.Feedback

    - - + + - - - - - - - - - - - - - + + + -
    NameNumberFieldType Description
    STATUS_UNKNOWN0STATUS_UNKNOWN should never be used. If used, an internal error has happened.
    STATUS_IS_SITTING1Robot is currently sitting.
    STATUS_IN_PROGRESS2Robot is trying to sit.statusStanceCommand.Feedback.Status

    +

    -
    -

    StanceCommand.Feedback.Status

    +
    +

    StanceCommand.Request

    - - + + - - - + + + - - - + + + + +
    NameNumberFieldType Description
    STATUS_UNKNOWN0end_timegoogle.protobuf.TimestampThe timestamp (in robot time) by which a command must finish executing. / This is a required field and used to prevent runaway commands.
    STATUS_STANCED1Robot has finished moving feet and they are at the specified positionstanceStance

    +
    +
    +

    StandCommand

    +

    The stand the robot up in its current position.

    +

    +
    +
    +

    StandCommand.Feedback

    +

    The StandCommand will provide feedback on whether or not the robot has finished +standing up.

    + + - - - + + + + + - - - + + + -
    STATUS_GOING_TO_STANCE2Robot is in the process of moving feet to specified positionFieldTypeDescription
    STATUS_TOO_FAR_AWAY3Robot is not moving, the specified stance was too far away. Hint: Try using SE2TrajectoryCommand to safely put the robot at the correct location first.statusStandCommand.Feedback.StatusCurrent status of the command.

    +

    -
    -

    StandCommand.Feedback.Status

    +
    +

    StandCommand.Request

    +

    Stand command request takes no additional arguments.

    +

    +
    +
    +

    StopCommand

    +

    Stop the robot in place with minimal motion.

    +

    +
    +
    +

    StopCommand.Feedback

    +

    Stop command provides no feedback.

    +

    +
    +
    +

    StopCommand.Request

    +

    Stop command request takes no additional arguments.

    +

    +
    +
    +

    ArmDragCommand.Feedback.Status

    @@ -2737,444 +2864,408 @@

    StandCommand.Feedback.Status

    - +

    -
    -

    bddf.proto

    -

    -
    -

    DataDescriptor

    -

    A DataDescriptor describes a data block which immediately follows it in the file. -A corresponding SeriesDescriptor with a matching series_index must precede this in the file.

    +
    +

    BatteryChangePoseCommand.Feedback.Status

    - - + + - - - + + + - - - + + + - - - + + + + + + + + -
    FieldTypeNameNumber Description
    series_indexuint32The series_index references the SeriesDescriptor to which the data following is associated.STATUS_UNKNOWN0
    timestampgoogle.protobuf.TimestampThe time at which the data is considered to be captured/sampled. E.g., the shutter-close time of a captured image.STATUS_COMPLETED1Robot is finished rolling
    additional_indexesint64Sometimes a visualizer will want to organize message by data timestamp, sometimes by the time messages were published or logged. The additional_indexes field allows extra indexes or timestamps to be associated with each data block for this purpose. Other identifying information may also be used here, such as the PID of the process which originated the data (e.g., for detecting if and when that process restarted). The values in this field should correspond to the labels defined in "additional_index_names" in the corresponding SeriesDescriptor.STATUS_IN_PROGRESS2Robot still in process of rolling over
    STATUS_FAILED3Robot has failed to roll onto its side

    +

    -
    -

    DescriptorBlock

    -

    A Descriptor block typically describes a series of messages, but the descriptor at the -start of the file describes the contents of the file as a whole, and the descriptor -at the end of the file is an index structure to allow efficient access to the contents -of the file.

    +
    +

    BatteryChangePoseCommand.Request.DirectionHint

    - - + + - - - - - - - - + + + - - - + + + - - - + + + -
    FieldTypeNameNumber Description
    file_descriptorFileFormatDescriptor
    series_descriptorSeriesDescriptorHINT_UNKNOWN0Unknown direction, just hold still
    series_block_indexSeriesBlockIndexHINT_RIGHT1Roll over right (right feet end up under the robot)
    file_indexFileIndexHINT_LEFT2Roll over left (left feet end up under the robot)

    +

    -
    -

    FileFormatDescriptor

    -

    The first block in the file should be a DescriptorBlock containing a FileFormatDescriptor. -FileFormatDescriptor indicates the file format version and annotations. -Annotations describe things like the robot from which the log was taken and the release id. -The format of annotation keys should be -{project-or-organization}/{annotation-name} -For example, ‘bosdyn/robot-serial-number’.

    +
    +

    ConstrainedManipulationCommand.Feedback.Status

    - - + + - - - + + + - - - + + + - - - + + + - - - + + + -
    FieldTypeNameNumber Description
    versionFileFormatVersionThe version number of the BDDF file.STATUS_UNKNOWN0STATUS_UNKNOWN should never be used. If used, an internal error has happened.
    annotationsFileFormatDescriptor.AnnotationsEntryFile/stream-wide annotations to describe the content of the file.STATUS_RUNNING1Constrained manipulation is working as expected
    checksum_typeFileFormatDescriptor.CheckSumTypeThe type of checksum supported by this stream. For BDDF version 1.0.0 this should be SHA1.STATUS_ARM_IS_STUCK2Arm is stuck, either force is being applied in a direction where the affordance can't move or not enough force is applied
    checksum_num_bytesuint32The number of bytes used for the BDDF checksum. For BDDF version 1.0.0 this should always be 20, even if CHECKSUM_NONE is used.STATUS_GRASP_IS_LOST3The grasp was lost. In this situation, constrained manipulation will stop applying force, and will hold the last position.

    +

    -
    -

    FileFormatDescriptor.AnnotationsEntry

    +
    +

    ConstrainedManipulationCommand.Request.TaskType

    +

    Geometrical category of a task. See the constrained_manipulation_helper function +for examples of each of these categories. For e.g. SE3_CIRCLE_FORCE_TORQUE corresponds +to lever type objects.

    - - + + - - + + - - - + + + - -
    FieldTypeNameNumber Description
    keystringTASK_TYPE_UNKNOWN0
    valuestringTASK_TYPE_SE3_CIRCLE_FORCE_TORQUE1This task type corresponds to circular tasks where both the end-effector position and orientation rotate about a circle to manipulate. The constrained manipulation logic will generate forces and torques in this case. Example tasks are: A lever or a ball valve with a solid grasp This task type will require an initial force vector specified in init_wrench_direction_in_frame_name. A torque vector can be specified as well if a good initial guess of the axis of rotation of the task is available.

    -
    -
    -

    FileFormatVersion

    -

    The current data file format is 1.0.0.

    - - - - - + + + - - - - - + + + - - - + + + - - - + + + + + + + + -
    FieldTypeDescriptionTASK_TYPE_R3_CIRCLE_EXTRADOF_FORCE2This task type corresponds to circular tasks that have an extra degree of freedom. In these tasks the end-effector position rotates about a circle but the orientation does not need to follow a circle (can remain fixed). The constrained manipulation logic will generate translational forces in this case. Example tasks are: A crank that has a loose handle and moves in a circle and the end-effector is free to rotate about the handle in one direction. This task type will require an initial force vector specified in init_wrench_direction_in_frame_name.
    major_versionuint32TASK_TYPE_SE3_ROTATIONAL_TORQUE3This task type corresponds to purely rotational tasks. In these tasks the orientation of the end-effector follows a circle, and the position remains fixed. The robot will apply a torque at the end-effector in these tasks. Example tasks are: rotating a knob or valve that does not have a lever arm. This task type will require an initial torque vector specified in init_wrench_direction_in_frame_name.
    minor_versionuint32TASK_TYPE_R3_CIRCLE_FORCE4This task type corresponds to circular tasks where the end-effector position and orientation rotate about a circle but the orientation does always strictly follow the circle due to slips. The constrained manipulation logic will generate translational forces in this case. Example tasks are: manipulating a cabinet where the grasp on handle is not very rigid or can often slip. This task type will require an initial force vector specified in init_wrench_direction_in_frame_name.
    patch_leveluint32TASK_TYPE_R3_LINEAR_FORCE5This task type corresponds to linear tasks where the end-effector position moves in a line but the orientation does not need to change. The constrained manipulation logic will generate a force in this case. Example tasks are: A crank that has a loose handle, or manipulating a cabinet where the grasp of the handle is loose and the end-effector is free to rotate about the handle in one direction. This task type will require an initial force vector specified in init_wrench_direction_in_frame_name.
    TASK_TYPE_HOLD_POSE6This option simply holds the hand in place with stiff impedance control. You can use this mode at the beginning of a constrained manipulation task or to hold position while transitioning between two different constrained manipulation tasks. The target pose to hold will be the measured hand pose upon transitioning to constrained manipulation or upon switching to this task type. This mode should only be used during constrained manipulation tasks, since it uses impedance control to hold the hand in place. This is not intended to stop the arm during position control moves.

    +

    -
    -

    FileIndex

    -

    As a file is closed, a DescriptorBlock containing a FileIndex should be written. -The FileIndex summarizes the data series stored in the file and the location of the -block-indexes for each type in the file. -Each series is assigned a “series_index” within the file, and this index may be used to -index into the repeated fields in this message. -E.g., for the series with series_index N, you can access its SeriesIdentifier by accessing -element N the of the series_identifiers repeated field.

    +
    +

    RobotCommandFeedbackStatus.Status

    - - + + - - - + + + - - - + + + - - - - - -
    FieldTypeNameNumber Description
    series_identifiersSeriesIdentifierSeriesIdentifer for each series in this file.STATUS_UNKNOWN0Behavior execution is in an unknown / unexpected state.
    series_block_index_offsetsuint64The offset from the start of the file of the SeriesBlockIndex block for each series.STATUS_PROCESSING1The robot is actively working on the command
    series_identifier_hashesuint64The hash of the series_identifier for each series.

    -
    -
    -

    MessageTypeDescriptor

    -

    If a data series contains a sequence of binary messages, the encoding and format of these -messages is described by a MesssageTypeDescriptor.

    - - - - - - + + + - - - - - + + + - - - + + + - - - + + + -
    FieldTypeDescriptionSTATUS_COMMAND_OVERRIDDEN2The command was replaced by a new command
    content_typestringDescription of the content type. E.g., "application/protobuf", "image/jpeg", "text/csv", ...STATUS_COMMAND_TIMED_OUT3The command expired
    type_namestringIf content_type is "application/protobuf", this is the full-name of the protobuf type.STATUS_ROBOT_FROZEN4The robot is in an unsafe state, and will only respond to known safe commands.
    is_metadataboolIf true, message contents are necessary for interpreting other messages. If the content of this file is split into multiple output files, these messages should be copied into each.STATUS_INCOMPATIBLE_HARDWARE5The request cannot be executed because the required hardware is missing. / For example, an armless robot receiving a synchronized command with an arm_command / request will return this value in the arm_command_feedback status.

    +

    -
    -

    PodTypeDescriptor

    -

    If a data series contains signals-style data of time-sampled “plain old datatypes”, this -describes the content of the series. -All POD data stored in data blocks is stored in little-endian byte order. -Any number of samples may be stored within a given data block.

    +
    +

    SE2TrajectoryCommand.Feedback.BodyMovementStatus

    - - + + - - - + + + - - - + + + + + + + + -
    FieldTypeNameNumber Description
    pod_typePodTypeEnumThe type of machine-readable values stored.BODY_STATUS_UNKNOWN0STATUS_UNKNOWN should never be used. If used, an internal error has happened.
    dimensionuint32If empty, indicates a single POD per sample. If one-element, indicates a vector of the given size per sample. If two-elements, indicates a matrix of the given size, and so on. An M x N x .. x P array of data is traversed from innermost (P) to outermost (M) dimension.BODY_STATUS_MOVING1The robot body is not settled at the goal.
    BODY_STATUS_SETTLED2The robot is at the goal and the body has stopped moving.

    +

    -
    -

    SeriesBlockIndex

    -

    This describes the location of the SeriesDescriptor DescriptorBlock for the series, and -the timestamp and location in the file of every data block in the series.

    +
    +

    SE2TrajectoryCommand.Feedback.Status

    - - + + - - - + + + - - - + + + - - - + + + - - - + + + -
    FieldTypeNameNumber Description
    series_indexuint32The series_index for the series described by this index block.STATUS_UNKNOWN0STATUS_UNKNOWN should never be used. If used, an internal error has happened.
    descriptor_file_offsetuint64Offset of type descriptor block from start of file.STATUS_AT_GOAL1The robot has arrived and is standing at the goal.
    block_entriesSeriesBlockIndex.BlockEntryThe timestamp and location of each data block for this series.STATUS_NEAR_GOAL3The robot has arrived at the goal and is doing final positioning.
    total_bytesuint64The total size of the data stored in the data blocks of this series.STATUS_GOING_TO_GOAL2The robot is attempting to go to a goal.

    +

    -
    -

    SeriesBlockIndex.BlockEntry

    +
    +

    SafePowerOffCommand.Feedback.Status

    - - + + - - - + + + - - - + + + - - - + + + -
    FieldTypeNameNumber Description
    timestampgoogle.protobuf.TimestampThe timestamp of data in this block.STATUS_UNKNOWN0STATUS_UNKNOWN should never be used. If used, an internal error has happened.
    file_offsetuint64The offset of the data block from the start of the file.STATUS_POWERED_OFF1Robot has powered off.
    additional_indexesint64Values of the additional indexes for describing this block.STATUS_IN_PROGRESS2Robot is trying to safely power off.

    +

    -
    -

    SeriesDescriptor

    -

    A description of a series of data blocks. -These data blocks may either represent binary messages of a variable size, or they may -represent a sequence of samples of POD data samples: single/vector/matrix/… of integer -or floating-point values.

    +
    +

    SitCommand.Feedback.Status

    - - + + - - - - - - - - + + + - - - + + + - - - + + + + +
    FieldTypeNameNumber Description
    series_indexuint32This index for the series is unique within the data file.
    series_identifierSeriesIdentifierThis is the globally unique {key -> value} mapping to identify the series.STATUS_UNKNOWN0STATUS_UNKNOWN should never be used. If used, an internal error has happened.
    identifier_hashuint64This is a hash of the series_identifier. The hash is the first 64 bits (read as a big-endian encoded uint64_t) of SHA1(S K1 V1 K2 V2 ...) where, - S is series identifier text, - K1 and V1 are the key and value of the first key and value of the spec, - K2 and V2 are the second key and value of the spec, etc... Here, all strings are encoded as utf-8, and keys are sorted lexicographically using this encoding (K1 < K2 < ...).STATUS_IS_SITTING1Robot is currently sitting.
    message_typeMessageTypeDescriptorSTATUS_IN_PROGRESS2Robot is trying to sit.

    +
    +
    +

    StanceCommand.Feedback.Status

    + + - - - + + + + + - - + + - - - + + + - - - + + + - - - + + + -
    pod_typePodTypeDescriptorNameNumberDescription
    struct_typeStructTypeDescriptorSTATUS_UNKNOWN0
    annotationsSeriesDescriptor.AnnotationsEntryAnnotations are a {key -> value} mapping for associating additional information with the series. The format of annotation keys should be {project-or-organization}/{annotation-name} For example, 'bosdyn/channel-name', 'bosdyn/protobuf-type'. Annotation keys without a '/' are reserved. The only current key in the reserved namespace is 'units': e.g., {'units': 'm/s2'}.STATUS_STANCED1Robot has finished moving feet and they are at the specified position
    additional_index_namesstringLabels for additional index values which should be attached to each DataDescriptor in the series. See the description of "additional_indexes" in DataDescriptor.STATUS_GOING_TO_STANCE2Robot is in the process of moving feet to specified position
    descriptionstringSTATUS_TOO_FAR_AWAY3Robot is not moving, the specified stance was too far away. Hint: Try using SE2TrajectoryCommand to safely put the robot at the correct location first.

    +

    -
    -

    SeriesDescriptor.AnnotationsEntry

    +
    +

    StandCommand.Feedback.Status

    - - + + - - - + + + - - - + + + + + + + + -
    FieldTypeNameNumber Description
    keystringSTATUS_UNKNOWN0STATUS_UNKNOWN should never be used. If used, an internal error has happened.
    valuestringSTATUS_IS_STANDING1Robot has finished standing up and has completed desired body trajectory. Robot is not attempting to move.
    STATUS_IN_PROGRESS2Robot is trying to come to a steady stand.

    +

    -
    -

    SeriesIdentifier

    -

    A key or description for selecting a message series. -Because there may be multiple ways of describing a message series, we identify -them by a unique mapping of {key -> value}. -A series_type corresponds to a set of keys which are expected in the mapping. -A ‘bosdyn:grpc:requests’ series_type, containing GRPC robot-id request messages, might -thus be specified as: -{’service’: ‘robot_id’, ‘message’: ‘bosdyn.api.RobotIdRequest’} -A ‘bosdyn:logtick’ series_type, containing a signals data variable from LogTick -annotations might be specified as: -{’varname’: ‘tablet.wifi.rssi’, ‘schema’: ‘tablet-comms’, ‘client’: ‘bd-tablet’}

    +
    +
    +

    bddf.proto

    +

    +
    +

    DataDescriptor

    +

    A DataDescriptor describes a data block which immediately follows it in the file. +A corresponding SeriesDescriptor with a matching series_index must precede this in the file.

    @@ -3185,20 +3276,29 @@

    SeriesIdentifierstring -

    + + + - - - + + + + + + + + -
    This is the kind of spec, which should correspond to a set of keys which are expected in the spec.series_indexuint32The series_index references the SeriesDescriptor to which the data following is associated.
    specSeriesIdentifier.SpecEntryThis is the "key" for naming the series within the file. A key->value description which should be unique for this series within the file with this series_type.timestampgoogle.protobuf.TimestampThe time at which the data is considered to be captured/sampled. E.g., the shutter-close time of a captured image.
    additional_indexesint64Sometimes a visualizer will want to organize message by data timestamp, sometimes by the time messages were published or logged. The additional_indexes field allows extra indexes or timestamps to be associated with each data block for this purpose. Other identifying information may also be used here, such as the PID of the process which originated the data (e.g., for detecting if and when that process restarted). The values in this field should correspond to the labels defined in "additional_index_names" in the corresponding SeriesDescriptor.

    +

    -
    -

    SeriesIdentifier.SpecEntry

    +
    +

    DescriptorBlock

    +

    A Descriptor block typically describes a series of messages, but the descriptor at the +start of the file describes the contents of the file as a whole, and the descriptor +at the end of the file is an index structure to allow efficient access to the contents +of the file.

    @@ -3209,28 +3309,36 @@

    SeriesIdentifier.SpecEntrystring +

    + - - + + + + + + + + + + + + -
    file_descriptorFileFormatDescriptor
    valuestringseries_descriptorSeriesDescriptor
    series_block_indexSeriesBlockIndex
    file_indexFileIndex

    +

    -
    -

    StructTypeDescriptor

    -

    A struct series is a composite formed by a set of other series whose messages or signals-ticks -are sampled at the same time. -For example, all there may be a struct series for a set of signals variables, all from a -process with an ‘update()’ function within which all all variables are sampled with the -same timestamp. -DataBlocks will not directly reference this series, but only child series of this series. -Struct series may reference other struct series, but the series structure must be a directed -acyclic graph (DAG): no circular reference structures.

    +
    +

    FileFormatDescriptor

    +

    The first block in the file should be a DescriptorBlock containing a FileFormatDescriptor. +FileFormatDescriptor indicates the file format version and annotations. +Annotations describe things like the robot from which the log was taken and the release id. +The format of annotation keys should be +{project-or-organization}/{annotation-name} +For example, ‘bosdyn/robot-serial-number’.

    @@ -3241,15 +3349,30 @@

    StructTypeDescriptor

    - - - + + + + + + + + + + + + + + + + + + -
    key_to_series_identifier_hashStructTypeDescriptor.KeyToSeriesIdentifierHashEntryA map of a name-reference to a series, identified by its series_identifer_hash.versionFileFormatVersionThe version number of the BDDF file.
    annotationsFileFormatDescriptor.AnnotationsEntryFile/stream-wide annotations to describe the content of the file.
    checksum_typeFileFormatDescriptor.CheckSumTypeThe type of checksum supported by this stream. For BDDF version 1.0.0 this should be SHA1.
    checksum_num_bytesuint32The number of bytes used for the BDDF checksum. For BDDF version 1.0.0 this should always be 20, even if CHECKSUM_NONE is used.

    +

    -
    -

    StructTypeDescriptor.KeyToSeriesIdentifierHashEntry

    +
    +

    FileFormatDescriptor.AnnotationsEntry

    @@ -3266,117 +3389,82 @@

    StructTypeDescriptor.KeyToSeriesIdentifierHashEntry

    - + -
    valueuint64string

    +

    -
    -

    FileFormatDescriptor.CheckSumType

    +
    +

    FileFormatVersion

    +

    The current data file format is 1.0.0.

    - - + + - - - + + + - - - + + + - - - + + + -
    NameNumberFieldType Description
    CHECKSUM_TYPE_UNKNOWN0Checksum type is unspecified. Should not be used.major_versionuint32
    CHECKSUM_TYPE_NONE1The writer of this stream is not computing a checksum. The stream checksum at the end of the file will be 160 bits all set to 0.minor_versionuint32
    CHECKSUM_TYPE_SHA12A 160 bit SHA1 checksum will be included at the end of the stream. This checksum will be computed over all data before digest itself at the end of the stream, and can be used to verify the stream was received uncorrupted.patch_leveluint32

    +

    -
    -

    PodTypeEnum

    -

    “Plain old data” types which may be stored within POD data blocks.

    +
    +

    FileIndex

    +

    As a file is closed, a DescriptorBlock containing a FileIndex should be written. +The FileIndex summarizes the data series stored in the file and the location of the +block-indexes for each type in the file. +Each series is assigned a “series_index” within the file, and this index may be used to +index into the repeated fields in this message. +E.g., for the series with series_index N, you can access its SeriesIdentifier by accessing +element N the of the series_identifiers repeated field.

    - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - + + + - - - + + + -
    NameNumberFieldType Description
    TYPE_UNSPECIFIED0
    TYPE_INT81
    TYPE_INT162
    TYPE_INT323
    TYPE_INT644
    TYPE_UINT85
    TYPE_UINT166
    TYPE_UINT327
    TYPE_UINT648series_identifiersSeriesIdentifierSeriesIdentifer for each series in this file.
    TYPE_FLOAT329series_block_index_offsetsuint64The offset from the start of the file of the SeriesBlockIndex block for each series.
    TYPE_FLOAT6410series_identifier_hashesuint64The hash of the series_identifier for each series.

    -
    +

    -
    -

    data_acquisition.proto

    -

    -
    -

    AcquireDataRequest

    +
    +

    MessageTypeDescriptor

    +

    If a data series contains a sequence of binary messages, the encoding and format of these +messages is described by a MesssageTypeDescriptor.

    @@ -3387,35 +3475,29 @@

    AcquireDataRequestRequestHeader -

    - - - - - - - - - - + + + - - - + + + - - - + + + -
    Common request header.
    action_idCaptureActionIdDefine the unique action that all data should be saved with.
    metadataMetadataMetadata to store with the data capture. The main DAQ service saves it in the DataBuffer.content_typestringDescription of the content type. E.g., "application/protobuf", "image/jpeg", "text/csv", ...
    acquisition_requestsAcquisitionRequestListList of capability requests that should be collected as part of this capture action.type_namestringIf content_type is "application/protobuf", this is the full-name of the protobuf type.
    min_timeoutgoogle.protobuf.DurationOptional duration used to extend the amount of time that the data request may take, in the event that a plugin is incorrectly specifying its timeout. The amount of time allowed will be the maximum of this duration and any requests made to plugins or other capture sources.is_metadataboolIf true, message contents are necessary for interpreting other messages. If the content of this file is split into multiple output files, these messages should be copied into each.

    +

    -
    -

    AcquireDataResponse

    +
    +

    PodTypeDescriptor

    +

    If a data series contains signals-style data of time-sampled “plain old datatypes”, this +describes the content of the series. +All POD data stored in data blocks is stored in little-endian byte order. +Any number of samples may be stored within a given data block.

    @@ -3426,26 +3508,22 @@

    AcquireDataResponseResponseHeader -

    + + + - - - - - - + - + -
    Common response headerpod_typePodTypeEnumThe type of machine-readable values stored.
    statusAcquireDataResponse.StatusResult of the AcquirePluginData RPC call. Further monitoring on the success of the acquisition request can be done using the GetStatus RPC.
    request_iddimension uint32Identifier which can be used to check the status of or cancel the acquisition action..If empty, indicates a single POD per sample. If one-element, indicates a vector of the given size per sample. If two-elements, indicates a matrix of the given size, and so on. An M x N x .. x P array of data is traversed from innermost (P) to outermost (M) dimension.

    +

    -
    -

    AcquirePluginDataRequest

    -

    Message sent by main DAQ service to all data acquisition plugin services.

    +
    +

    SeriesBlockIndex

    +

    This describes the location of the SeriesDescriptor DescriptorBlock for the series, and +the timestamp and location in the file of every data block in the series.

    @@ -3456,35 +3534,30 @@

    AcquirePluginDataRequestRequestHeader -

    - - - - - + + + - - - + + + - - - + + + - - - + + + -
    Common request header
    data_idDataIdentifierMetadata acquirers use these DataIdentifier objects to associate them with the acquired metadata when storing them in the DataBuffer. Data acquirers simply get the timestamp from these DataIdentifier objects to use when storing the data in the DataBuffer.series_indexuint32The series_index for the series described by this index block.
    metadataMetadataMetadata specified by the requestor.descriptor_file_offsetuint64Offset of type descriptor block from start of file.
    action_idCaptureActionIdId to be associated with all the data buffered for this request. It will be stored in the DataIdentifier field of each piece of data buffered from this request.block_entriesSeriesBlockIndex.BlockEntryThe timestamp and location of each data block for this series.
    acquisition_requestsAcquisitionRequestListList of capability requests specific for this DAQ plugin.total_bytesuint64The total size of the data stored in the data blocks of this series.

    +

    -
    -

    AcquirePluginDataResponse

    +
    +

    SeriesBlockIndex.BlockEntry

    @@ -3495,32 +3568,29 @@

    AcquirePluginDataResponseResponseHeader -

    - - - - - + + + - - - + + + - - - + + + -
    Common response header
    statusAcquirePluginDataResponse.StatusResult of the AcquirePluginData RPC call. Further monitoring on the success of the acquisition request can be done using the GetStatus RPC.timestampgoogle.protobuf.TimestampThe timestamp of data in this block.
    request_iduint32Identifier which can be used to check the status of or cancel the acquisition action..file_offsetuint64The offset of the data block from the start of the file.
    timeout_deadlinegoogle.protobuf.TimestampTime (in the robot's clock) by which this capture should definitely be complete. If it is not complete by this time, something has gone wrong.additional_indexesint64Values of the additional indexes for describing this block.

    +

    -
    -

    AcquisitionCapabilityList

    -

    A list of all capabilities (data and images) that a specific data acquisition plugin service can successfully -acquire and save the data specified in each capability.

    +
    +

    SeriesDescriptor

    +

    A description of a series of data blocks. +These data blocks may either represent binary messages of a variable size, or they may +represent a sequence of samples of POD data samples: single/vector/matrix/… of integer +or floating-point values.

    @@ -3531,47 +3601,55 @@

    AcquisitionCapabilityListDataAcquisitionCapability -

    + + + - - - + + + - -
    List of non-image data acquisition capabilities.series_indexuint32This index for the series is unique within the data file.
    image_sourcesImageAcquisitionCapabilityList of image data acquisition capabilities.series_identifierSeriesIdentifierThis is the globally unique {key -> value} mapping to identify the series.

    -
    -
    -

    AcquisitionRequestList

    -

    The grouping of all individual image and data captures for a given capture action.

    - - - - - + + + - - - - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + -
    FieldTypeDescriptionidentifier_hashuint64This is a hash of the series_identifier. The hash is the first 64 bits (read as a big-endian encoded uint64_t) of SHA1(S K1 V1 K2 V2 ...) where, - S is series identifier text, - K1 and V1 are the key and value of the first key and value of the spec, - K2 and V2 are the second key and value of the spec, etc... Here, all strings are encoded as utf-8, and keys are sorted lexicographically using this encoding (K1 < K2 < ...).
    image_capturesImageSourceCaptureList of image requests.message_typeMessageTypeDescriptor
    data_capturesDataCaptureList of non-image data and metadata requests.pod_typePodTypeDescriptor
    struct_typeStructTypeDescriptor
    annotationsSeriesDescriptor.AnnotationsEntryAnnotations are a {key -> value} mapping for associating additional information with the series. The format of annotation keys should be {project-or-organization}/{annotation-name} For example, 'bosdyn/channel-name', 'bosdyn/protobuf-type'. Annotation keys without a '/' are reserved. The only current key in the reserved namespace is 'units': e.g., {'units': 'm/s2'}.
    additional_index_namesstringLabels for additional index values which should be attached to each DataDescriptor in the series. See the description of "additional_indexes" in DataDescriptor.
    descriptionstring

    +

    -
    -

    AssociatedMetadata

    -

    This message can be stored as a DataBlob in the data buffer in order to be recognized as -metadata that is associated with previously stored data.

    +
    +

    SeriesDescriptor.AnnotationsEntry

    @@ -3582,20 +3660,30 @@

    AssociatedMetadataDataIdentifier -

    + + + - - - + + + -
    The data that this metadata refers to. If only the action_id is filled out, this metadata is associated with the entire capture action.keystring
    metadataMetadataMetadata message to be stored.valuestring

    +

    -
    -

    CancelAcquisitionRequest

    +
    +

    SeriesIdentifier

    +

    A key or description for selecting a message series. +Because there may be multiple ways of describing a message series, we identify +them by a unique mapping of {key -> value}. +A series_type corresponds to a set of keys which are expected in the mapping. +A ‘bosdyn:grpc:requests’ series_type, containing GRPC robot-id request messages, might +thus be specified as: +{’service’: ‘robot_id’, ‘message’: ‘bosdyn.api.RobotIdRequest’} +A ‘bosdyn:logtick’ series_type, containing a signals data variable from LogTick +annotations might be specified as: +{’varname’: ‘tablet.wifi.rssi’, ‘schema’: ‘tablet-comms’, ‘client’: ‘bd-tablet’}

    @@ -3606,20 +3694,20 @@

    CancelAcquisitionRequestRequestHeader -

    + + + - - - + + + -
    Common request headerseries_typestringThis is the kind of spec, which should correspond to a set of keys which are expected in the spec.
    request_iduint32Which acquisition request to cancel.specSeriesIdentifier.SpecEntryThis is the "key" for naming the series within the file. A key->value description which should be unique for this series within the file with this series_type.

    +

    -
    -

    CancelAcquisitionResponse

    +
    +

    SeriesIdentifier.SpecEntry

    @@ -3630,22 +3718,28 @@

    CancelAcquisitionResponseResponseHeader -

    + + + - - - + + + -
    Common response headerkeystring
    statusCancelAcquisitionResponse.StatusThe status of the Cancellation RPC. Further monitoring on the success of the cancellation request can be done using the GetStatus RPC.valuestring

    +

    -
    -

    CaptureActionId

    -

    The CaptureActionId describes the entire capture action for an AcquireData request and will be used -to uniquely identify that full request’s set of stored data.

    +
    +

    StructTypeDescriptor

    +

    A struct series is a composite formed by a set of other series whose messages or signals-ticks +are sampled at the same time. +For example, all there may be a struct series for a set of signals variables, all from a +process with an ‘update()’ function within which all all variables are sampled with the +same timestamp. +DataBlocks will not directly reference this series, but only child series of this series. +Struct series may reference other struct series, but the series structure must be a directed +acyclic graph (DAG): no circular reference structures.

    @@ -3656,27 +3750,15 @@

    CaptureActionIdstring -

    - - - - - - - - - - + + + -
    The action name is used to group all pieces of data associated with a single AcquireData request. The action name must be unique for the given group name, meaning no two actions with the same group name can have the same action name.
    group_namestringThe group name is used to group a "session" of data, such as a mission or a teleop capture session, which includes multiple capture actions (from multiple AcquireData RPCs).
    timestampgoogle.protobuf.TimestampTime (in the robot's clock) at which this capture was triggered. If the timestamp is not specified in the AcquireData RPC, the main data acquisition service on robot will populate the timestamp field with the timestamp of when the RPC was recieved.key_to_series_identifier_hashStructTypeDescriptor.KeyToSeriesIdentifierHashEntryA map of a name-reference to a series, identified by its series_identifer_hash.

    +

    -
    -

    DataAcquisitionCapability

    -

    Description of a data acquisition capability. A data acquisition plugin service will have a -set of capabilities for which it can acquire and save the appropriate data.

    +
    +

    StructTypeDescriptor.KeyToSeriesIdentifierHashEntry

    @@ -3687,106 +3769,123 @@

    DataAcquisitionCapabilitystring -

    + - - - - - - - - + + + -
    Unique identifier for the data acquisition capability. Used for identification purposes when making acquire data requests.
    descriptionstringA human readable name of the data acquisition capability that will be shown on the tablet.
    channel_namestringChannel name that will be associated with all data stored in the data buffer through each data acquisition plugin. Metadata acquirers do not specify this field.valueuint64

    +

    -
    -

    DataCapture

    -

    An individual capture which can be specified in the AcquireData request to identify a piece of -non-image data to be collected.

    +
    +

    FileFormatDescriptor.CheckSumType

    - - + + - - - + + + + + + + + + + + + + -
    FieldTypeNameNumber Description
    namestringName of the data to be captured. This should match the uniquely identifying name from the DataAcquisitionCapability.CHECKSUM_TYPE_UNKNOWN0Checksum type is unspecified. Should not be used.
    CHECKSUM_TYPE_NONE1The writer of this stream is not computing a checksum. The stream checksum at the end of the file will be 160 bits all set to 0.
    CHECKSUM_TYPE_SHA12A 160 bit SHA1 checksum will be included at the end of the stream. This checksum will be computed over all data before digest itself at the end of the stream, and can be used to verify the stream was received uncorrupted.

    +

    -
    -

    DataError

    -

    An error associated with a particular capture action and piece of data.

    +
    +

    PodTypeEnum

    +

    “Plain old data” types which may be stored within POD data blocks.

    - - + + - - - + + + - - - + + + - - - + + + - -
    FieldTypeNameNumber Description
    data_idDataIdentifierIdentifier for the data to be saved.TYPE_UNSPECIFIED0
    error_messagestringHuman-readable message describing the error.TYPE_INT81
    error_datagoogle.protobuf.AnyCustom plugin-specific data about the problem.TYPE_INT162

    -
    -
    -

    DataIdentifier

    -

    A way to identify an individual piece of data stored in the data buffer.

    - - - - - + + + - - - - - + + + - - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + -
    FieldTypeDescriptionTYPE_INT323
    action_idCaptureActionIdThe action where the data was acquired.TYPE_INT644
    channelstringData buffer channel associated with the DataBlob. The channel is used to group data across actions by a specific source, and it can be used in queries to retrieve some subset of data. For example, the channel could be "ptz" and queries can be made for all PTZ images.TYPE_UINT85
    data_namestringData-specific identifier that can optionally be used to disambiguate cases where the action_id and channel are insufficient. For example, you save cropped SpotCAM pano image that are detected as gauges to a "detected_gauges" channel, but want a way to further individually identify them as each specific gauge, so you give each detection a unique data_name.TYPE_UINT166
    TYPE_UINT327
    TYPE_UINT648
    TYPE_FLOAT329
    TYPE_FLOAT6410

    +

    -
    -

    GetServiceInfoRequest

    +
    +
    +

    data_acquisition.proto

    +

    +
    +

    AcquireDataRequest

    @@ -3799,13 +3898,33 @@

    GetServiceInfoRequest

    - + + + + + + + + + + + + + + + + + + + + + -
    header RequestHeaderCommon request headerCommon request header.
    action_idCaptureActionIdDefine the unique action that all data should be saved with.
    metadataMetadataMetadata to store with the data capture. The main DAQ service saves it in the DataBuffer.
    acquisition_requestsAcquisitionRequestListList of capability requests that should be collected as part of this capture action.
    min_timeoutgoogle.protobuf.DurationOptional duration used to extend the amount of time that the data request may take, in the event that a plugin is incorrectly specifying its timeout. The amount of time allowed will be the maximum of this duration and any requests made to plugins or other capture sources.

    +

    -
    -

    GetServiceInfoResponse

    +
    +

    AcquireDataResponse

    @@ -3818,18 +3937,24 @@

    GetServiceInfoResponse

    - + - - - + + + + + + + + -
    header ResponseHeaderCommon response header.Common response header
    capabilitiesAcquisitionCapabilityListList of capabilities that the data acquisition (plugin) service can collect and save data for.statusAcquireDataResponse.StatusResult of the AcquirePluginData RPC call. Further monitoring on the success of the acquisition request can be done using the GetStatus RPC.
    request_iduint32Identifier which can be used to check the status of or cancel the acquisition action..

    +

    -
    -

    GetStatusRequest

    +
    +

    AcquirePluginDataRequest

    +

    Message sent by main DAQ service to all data acquisition plugin services.

    @@ -3845,15 +3970,30 @@

    GetStatusRequestuint32 -

    + + + + + + + + + + + + + + + + + + -
    Which acquisition to check the status of.data_idDataIdentifierMetadata acquirers use these DataIdentifier objects to associate them with the acquired metadata when storing them in the DataBuffer. Data acquirers simply get the timestamp from these DataIdentifier objects to use when storing the data in the DataBuffer.
    metadataMetadataMetadata specified by the requestor.
    action_idCaptureActionIdId to be associated with all the data buffered for this request. It will be stored in the DataIdentifier field of each piece of data buffered from this request.
    acquisition_requestsAcquisitionRequestListList of capability requests specific for this DAQ plugin.

    +

    -
    -

    GetStatusResponse

    +
    +

    AcquirePluginDataResponse

    @@ -3870,32 +4010,26 @@

    GetStatusResponseGetStatusResponse.Status -

    - - - - - + + - - - + + + - - - + + + -
    data_savedDataIdentifierData that has been successfully saved into the data buffer for the capture action.AcquirePluginDataResponse.StatusResult of the AcquirePluginData RPC call. Further monitoring on the success of the acquisition request can be done using the GetStatus RPC.
    data_errorsDataErrorA list of data captures which have failed in some way during the action. For example, the data acquisition plugin is unable to communicate to a sensor and responds with a data error for that specific data capture.request_iduint32Identifier which can be used to check the status of or cancel the acquisition action..
    service_errorsPluginServiceErrorServices which failed independent of a particular data id. For example, if a plugin times out or crashes, it could be reported here.timeout_deadlinegoogle.protobuf.TimestampTime (in the robot's clock) by which this capture should definitely be complete. If it is not complete by this time, something has gone wrong.

    +

    -
    -

    ImageAcquisitionCapability

    -

    Description of an image acquisition capability. The image acquisition capabilities will be available -through the main data acquisition service on robot and are populated based on all bosdyn.api.ImageService -services registered to the robot’s directory.

    +
    +

    AcquisitionCapabilityList

    +

    A list of all capabilities (data and images) that a specific data acquisition plugin service can successfully +acquire and save the data specified in each capability.

    @@ -3906,22 +4040,21 @@

    ImageAcquisitionCapabilitystring -

    + + + - - - + + + -
    The image service's service name used in directory registration.data_sourcesDataAcquisitionCapabilityList of non-image data acquisition capabilities.
    image_source_namesstringList of the image source names reported by the image service (through the ListImageSources RPC).image_sourcesImageAcquisitionCapabilityList of image data acquisition capabilities.

    +

    -
    -

    ImageSourceCapture

    -

    An individual capture which can be specified in the AcquireData request to identify a piece of -image data to be collected.

    +
    +

    AcquisitionRequestList

    +

    The grouping of all individual image and data captures for a given capture action.

    @@ -3932,22 +4065,22 @@

    ImageSourceCapturestring -

    + + + - - - + + + -
    Name of the image service that the data should be requested from.image_capturesImageSourceCaptureList of image requests.
    image_sourcestringSpecific image source to use from the list reported by the image service within the ImageAcquisitionCapability message.data_capturesDataCaptureList of non-image data and metadata requests.

    +

    -
    -

    Metadata

    -

    Structured data that can be included within a AcquireData RPC and saved in association with -that capture action.

    +
    +

    AssociatedMetadata

    +

    This message can be stored as a DataBlob in the data buffer in order to be recognized as +metadata that is associated with previously stored data.

    @@ -3958,16 +4091,20 @@

    Metadatagoogle.protobuf.Struct -

    + + + + + + + + -
    JSON representation of metadata.reference_idDataIdentifierThe data that this metadata refers to. The timestamp field is ignored. If only the action_id is filled out, this metadata is associated with the entire capture action.
    metadataMetadataMetadata message to be stored.

    +

    -
    -

    PluginServiceError

    -

    An error associated with a particular data acquisition plugin service that was

    +
    +

    CancelAcquisitionRequest

    @@ -3978,319 +4115,301 @@

    PluginServiceErrorstring -

    - - - - - + + + - - - + + + -
    Name of the service with the error
    errorPluginServiceError.ErrorCodeFailure mode.headerRequestHeaderCommon request header
    messagestringDescription of the error.request_iduint32Which acquisition request to cancel.

    +

    -
    -

    AcquireDataResponse.Status

    +
    +

    CancelAcquisitionResponse

    - - + + - - - - - - - - + + + - - - + + + -
    NameNumberFieldType Description
    STATUS_UNKNOWN0
    STATUS_OK1The capture action has successfully started acquiring the data.headerResponseHeaderCommon response header
    STATUS_UNKNOWN_CAPTURE_TYPE2One of the capability requests in the AcquisitionRequestList is unknown.statusCancelAcquisitionResponse.StatusThe status of the Cancellation RPC. Further monitoring on the success of the cancellation request can be done using the GetStatus RPC.

    +

    -
    -

    AcquirePluginDataResponse.Status

    +
    +

    CaptureActionId

    +

    The CaptureActionId describes the entire capture action for an AcquireData request and will be used +to uniquely identify that full request’s set of stored data.

    - - + + - - - + + + - - - + + + - - - + + + -
    NameNumberFieldType Description
    STATUS_UNKNOWN0action_namestringThe action name is used to group all pieces of data associated with a single AcquireData request. The action name must be unique for the given group name, meaning no two actions with the same group name can have the same action name.
    STATUS_OK1The capture action has successfully started acquiring the data.group_namestringThe group name is used to group a "session" of data, such as a mission or a teleop capture session, which includes multiple capture actions (from multiple AcquireData RPCs).
    STATUS_UNKNOWN_CAPTURE_TYPE2One of the capability requests in the AcquisitionRequestList is unknown.timestampgoogle.protobuf.TimestampTime (in the robot's clock) at which this capture was triggered. If the timestamp is not specified in the AcquireData RPC, the main data acquisition service on robot will populate the timestamp field with the timestamp of when the RPC was recieved.

    +

    -
    -

    CancelAcquisitionResponse.Status

    +
    +

    DataAcquisitionCapability

    +

    Description of a data acquisition capability. A data acquisition plugin service will have a +set of capabilities for which it can acquire and save the appropriate data.

    - - + + - - - + + + - - - + + + - - - + + + - - - + + + -
    NameNumberFieldType Description
    STATUS_UNKNOWN0namestringUnique identifier for the data acquisition capability. Used for identification purposes when making acquire data requests.
    STATUS_OK1Successfully cancelled the data acquisition request.descriptionstringA human readable name of the data acquisition capability that will be shown on the tablet.
    STATUS_FAILED_TO_CANCEL2Unable to stop the data acquisition request.channel_namestringChannel name that will be associated with all data stored in the data buffer through each data acquisition plugin. Metadata acquirers do not specify this field.
    STATUS_REQUEST_ID_DOES_NOT_EXIST3[Error] The request_id does not exist.service_namestringThe data acquisition plugin service's service name used in directory registration.

    +

    -
    -

    GetStatusResponse.Status

    +
    +

    DataCapture

    +

    An individual capture which can be specified in the AcquireData request to identify a piece of +non-image data to be collected.

    - - + + - - - - - - - - - - - - - + + + + +
    NameNumberFieldType Description
    STATUS_UNKNOWN0
    STATUS_ACQUIRING1[Status] Data acquisition is still in progress
    STATUS_SAVING2[Status] Data has been acquired, processing and storage is now in progress.namestringName of the data to be captured. This should match the uniquely identifying name from the DataAcquisitionCapability.

    +
    +
    +

    DataError

    +

    An error associated with a particular capture action and piece of data.

    + + - - - + + + + + - - - + + + - - - + + + - - - + + + + +
    STATUS_COMPLETE3[Status] Data acquisition is complete.FieldTypeDescription
    STATUS_CANCEL_IN_PROGRESS4[Status] The data acquisition service is working to cancel the request.data_idDataIdentifierIdentifier for the data to be saved.
    STATUS_ACQUISITION_CANCELLED5[Status] The data acquisition request was cancelled manually.error_messagestringHuman-readable message describing the error.
    STATUS_DATA_ERROR10[Error - AcquireData] An error occurred while acquiring, processing, or saving data.error_datagoogle.protobuf.AnyCustom plugin-specific data about the problem.

    +
    +
    +

    DataIdentifier

    +

    A way to identify an individual piece of data stored in the data buffer.

    + + - - - + + + + + - - - + + + - - - + + + - - - + + + -
    STATUS_TIMEDOUT11[Error - AcquireData] The data collection has passed the deadline for completion.FieldTypeDescription
    STATUS_INTERNAL_ERROR12[Error - AcquireData] An error occurred such that we don't even know our status.action_idCaptureActionIdThe action where the data was acquired.
    STATUS_CANCEL_ACQUISITION_FAILED30[Error -CancelAcquisition] The cancellation request failed to complete.channelstringData buffer channel associated with the DataBlob. The channel is used to group data across actions by a specific source, and it can be used in queries to retrieve some subset of data. For example, the channel could be "ptz" and queries can be made for all PTZ images.
    STATUS_REQUEST_ID_DOES_NOT_EXIST20[Error - GetStatus] The request_id does not exist.data_namestringData-specific identifier that can optionally be used to disambiguate cases where the action_id and channel are insufficient. For example, you save cropped SpotCAM pano image that are detected as gauges to a "detected_gauges" channel, but want a way to further individually identify them as each specific gauge, so you give each detection a unique data_name.

    +

    -
    -

    PluginServiceError.ErrorCode

    -

    Possible ways a plugin can fail.

    +
    +

    GetServiceInfoRequest

    - - + + - - - + + + + +
    NameNumberFieldType Description
    STATUS_UNKNOWN0headerRequestHeaderCommon request header

    +
    +
    +

    GetServiceInfoResponse

    + + - - - + + + + + - - - + + + - - - + + + -
    STATUS_REQUEST_ERROR1The initial RPC to the plugin failedFieldTypeDescription
    STATUS_GETSTATUS_ERROR2The GetStatus request to the plugin failed with a data error or timeout.headerResponseHeaderCommon response header.
    STATUS_INTERNAL_ERROR3The plugin reported an internal error.capabilitiesAcquisitionCapabilityListList of capabilities that the data acquisition (plugin) service can collect and save data for.

    -
    +

    -
    -

    data_acquisition_plugin_service.proto

    -

    -
    -

    DataAcquisitionPluginService

    -

    The DataAcquisitionPluginService is a gRPC service that a payload developer implements to retrieve -data from a sensor (or more generally perform some payload action) and optionally store that data -on the robot via the DataAcquisitionStore service.

    +
    +

    GetStatusRequest

    - - - + + - - - - - - - - - - - - - - - - + + + - - - - + + + -
    Method NameRequest TypeResponse TypeFieldType Description
    AcquirePluginDataAcquirePluginDataRequestAcquirePluginDataResponseTrigger a data acquisition to save metadata and non-image data to the data buffer. Sent by the main DAQ as a result of a data acquisition request from the tablet or a client.
    GetStatusGetStatusRequestGetStatusResponseQuery the status of a data acquisition.
    GetServiceInfoGetServiceInfoRequestGetServiceInfoResponseGet information from a DAQ service; lists acquisition capabilities.headerRequestHeaderCommon request header
    CancelAcquisitionCancelAcquisitionRequestCancelAcquisitionResponseCancel an in-progress data acquisition.request_iduint32Which acquisition to check the status of.

    -
    +

    -
    -

    data_acquisition_service.proto

    -

    -
    -

    DataAcquisitionService

    -

    The DataAcquisitionService is the main data acquisition service run on robot, which recieves -incoming requests and sends queries to all directory-registered DataAcquisitionPluginServices.

    +
    +

    GetStatusResponse

    - - - + + - - - - + + + - - - - + + + - - - - + + + - - - - + + + + + + + + -
    Method NameRequest TypeResponse TypeFieldType Description
    AcquireDataAcquireDataRequestAcquireDataResponseTrigger a data acquisition to save data and metadata to the data buffer. Sent by the tablet or a client to initiate a data acquisition and buffering process.headerResponseHeaderCommon response header
    GetStatusGetStatusRequestGetStatusResponseQuery the status of a data acquisition.statusGetStatusResponse.Status
    GetServiceInfoGetServiceInfoRequestGetServiceInfoResponseGet information from a DAQ service; lists acquisition capabilities.data_savedDataIdentifierData that has been successfully saved into the data buffer for the capture action.
    CancelAcquisitionCancelAcquisitionRequestCancelAcquisitionResponseCancel an in-progress data acquisition.data_errorsDataErrorA list of data captures which have failed in some way during the action. For example, the data acquisition plugin is unable to communicate to a sensor and responds with a data error for that specific data capture.
    service_errorsPluginServiceErrorServices which failed independent of a particular data id. For example, if a plugin times out or crashes, it could be reported here.

    -
    +

    -
    -

    data_acquisition_store.proto

    -

    -
    -

    ActionIdQuery

    -

    A query parameter which filters the possible set of data identifiters to those -which contain the same action/group names matching any of the names in the -set of CaptureActionIds.

    +
    +

    ImageAcquisitionCapability

    +

    Description of an image acquisition capability. The image acquisition capabilities will be available +through the main data acquisition service on robot and are populated based on all bosdyn.api.ImageService +services registered to the robot’s directory.

    @@ -4301,17 +4420,22 @@

    ActionIdQueryCaptureActionId -

    + + + + + + + + -
    The action ids to filter with.service_namestringThe image service's service name used in directory registration.
    image_source_namesstringList of the image source names reported by the image service (through the ListImageSources RPC).

    +

    -
    -

    DataQueryParams

    -

    The message containing the different query parameters which can be applied to -the ListData requests.

    +
    +

    ImageSourceCapture

    +

    An individual capture which can be specified in the AcquireData request to identify a piece of +image data to be collected.

    @@ -4322,20 +4446,22 @@

    DataQueryParamsTimeRangeQuery -

    + + + - - - + + + -
    Time range to query.image_servicestringName of the image service that the data should be requested from.
    action_idsActionIdQueryList of action ids to query.image_sourcestringSpecific image source to use from the list reported by the image service within the ImageAcquisitionCapability message.

    +

    -
    -

    ListCaptureActionsRequest

    +
    +

    Metadata

    +

    Structured data that can be included within a AcquireData RPC and saved in association with +that capture action.

    @@ -4346,20 +4472,16 @@

    ListCaptureActionsRequestRequestHeader -

    - - - - - + + + -
    Common request header.
    queryDataQueryParamsQuery parameters for finding action ids.datagoogle.protobuf.StructJSON representation of metadata.

    +

    -
    -

    ListCaptureActionsResponse

    +
    +

    PluginServiceError

    +

    An error associated with a particular data acquisition plugin service that was

    @@ -4370,294 +4492,319 @@

    ListCaptureActionsResponseResponseHeader -

    + + + - - - + + + + + + + + -
    Common response header.service_namestringName of the service with the error
    action_idsCaptureActionIdList of action ids that satisfied the query parameters.errorPluginServiceError.ErrorCodeFailure mode.
    messagestringDescription of the error.

    +

    -
    -

    ListStoredDataRequest

    +
    +

    AcquireDataResponse.Status

    - - + + - - - + + + - - - + + + + + + + + -
    FieldTypeNameNumber Description
    headerRequestHeaderCommon request header.STATUS_UNKNOWN0
    queryDataQueryParamsQuery parameters for finding data.STATUS_OK1The capture action has successfully started acquiring the data.
    STATUS_UNKNOWN_CAPTURE_TYPE2One of the capability requests in the AcquisitionRequestList is unknown.

    +

    -
    -

    ListStoredDataResponse

    +
    +

    AcquirePluginDataResponse.Status

    - - + + - - - + + + - - - + + + + + + + + -
    FieldTypeNameNumber Description
    headerResponseHeaderCommon response header.STATUS_UNKNOWN0
    data_idsDataIdentifierList of data identifiers that satisfied the query parameters.STATUS_OK1The capture action has successfully started acquiring the data.
    STATUS_UNKNOWN_CAPTURE_TYPE2One of the capability requests in the AcquisitionRequestList is unknown.

    +

    -
    -

    ListStoredImagesRequest

    +
    +

    CancelAcquisitionResponse.Status

    - - + + - - - - - - - - + + + - -
    FieldTypeNameNumber Description
    headerRequestHeaderCommon request header.
    queryDataQueryParamsQuery parameters for finding images.STATUS_UNKNOWN0

    -
    -
    -

    ListStoredImagesResponse

    - - - - - + + + - - - - - + + + - - - + + + -
    FieldTypeDescriptionSTATUS_OK1Successfully cancelled the data acquisition request.
    headerResponseHeaderCommon response header.STATUS_FAILED_TO_CANCEL2Unable to stop the data acquisition request.
    data_idsDataIdentifierList of image data identifiers that satisfied the query parameters.STATUS_REQUEST_ID_DOES_NOT_EXIST3[Error] The request_id does not exist.

    +

    -
    -

    ListStoredMetadataRequest

    +
    +

    GetStatusResponse.Status

    - - + + - - - + + + - - - + + + - -
    FieldTypeNameNumber Description
    headerRequestHeaderCommon request header.STATUS_UNKNOWN0
    queryDataQueryParamsQuery parameters for finding metadata.STATUS_ACQUIRING1[Status] Data acquisition is still in progress

    -
    -
    -

    ListStoredMetadataResponse

    - - - - - + + + - - - - - + + + - - - + + + - -
    FieldTypeDescriptionSTATUS_SAVING2[Status] Data has been acquired, processing and storage is now in progress.
    headerResponseHeaderCommon response header.STATUS_COMPLETE3[Status] Data acquisition is complete.
    data_idsDataIdentifierList of metadata data identifiers that satisfied the query parameters.STATUS_CANCEL_IN_PROGRESS4[Status] The data acquisition service is working to cancel the request.

    -
    -
    -

    StoreDataRequest

    - - - - - + + + - - - - - + + + - - - + + + - - - + + + - - - + + + + + + + + -
    FieldTypeDescriptionSTATUS_ACQUISITION_CANCELLED5[Status] The data acquisition request was cancelled manually.
    headerRequestHeaderCommon request header.STATUS_DATA_ERROR10[Error - AcquireData] An error occurred while acquiring, processing, or saving data.
    databytesData to store.STATUS_TIMEDOUT11[Error - AcquireData] The data collection has passed the deadline for completion.
    data_idDataIdentifierData identifier of the data.STATUS_INTERNAL_ERROR12[Error - AcquireData] An error occurred such that we don't even know our status.
    file_extensionstringFile extension to use when writing the data to file.STATUS_CANCEL_ACQUISITION_FAILED30[Error -CancelAcquisition] The cancellation request failed to complete.
    STATUS_REQUEST_ID_DOES_NOT_EXIST20[Error - GetStatus] The request_id does not exist.

    +

    -
    -

    StoreDataResponse

    +
    +

    PluginServiceError.ErrorCode

    +

    Possible ways a plugin can fail.

    - - + + - - - + + + + + + + + + + + + + + + + + + -
    FieldTypeNameNumber Description
    headerResponseHeaderCommon response header.STATUS_UNKNOWN0
    STATUS_REQUEST_ERROR1The initial RPC to the plugin failed
    STATUS_GETSTATUS_ERROR2The GetStatus request to the plugin failed with a data error or timeout.
    STATUS_INTERNAL_ERROR3The plugin reported an internal error.

    +

    -
    -

    StoreImageRequest

    +
    +
    +

    data_acquisition_plugin_service.proto

    +

    +
    +

    DataAcquisitionPluginService

    +

    The DataAcquisitionPluginService is a gRPC service that a payload developer implements to retrieve +data from a sensor (or more generally perform some payload action) and optionally store that data +on the robot via the DataAcquisitionStore service.

    - - + + + - - - + + + + - - - + + + + - - - + + + + + + + + + + -
    FieldTypeMethod NameRequest TypeResponse Type Description
    headerRequestHeaderCommon request header.AcquirePluginDataAcquirePluginDataRequestAcquirePluginDataResponseTrigger a data acquisition to save metadata and non-image data to the data buffer. Sent by the main DAQ as a result of a data acquisition request from the tablet or a client.
    imageImageCaptureImage to store.GetStatusGetStatusRequestGetStatusResponseQuery the status of a data acquisition.
    data_idDataIdentifierData identifier of the image.GetServiceInfoGetServiceInfoRequestGetServiceInfoResponseGet information from a DAQ service; lists acquisition capabilities.
    CancelAcquisitionCancelAcquisitionRequestCancelAcquisitionResponseCancel an in-progress data acquisition.

    +

    -
    -

    StoreImageResponse

    +
    +
    +

    data_acquisition_service.proto

    +

    +
    +

    DataAcquisitionService

    +

    The DataAcquisitionService is the main data acquisition service run on robot, which recieves +incoming requests and sends queries to all directory-registered DataAcquisitionPluginServices.

    - - + + + - - - - - -
    FieldTypeMethod NameRequest TypeResponse Type Description
    headerResponseHeaderCommon response header.

    -
    -
    -

    StoreMetadataRequest

    - - - - - - + + + + - - - - - + + + + - - - + + + + - - - + + + + -
    FieldTypeDescriptionAcquireDataAcquireDataRequestAcquireDataResponseTrigger a data acquisition to save data and metadata to the data buffer. Sent by the tablet or a client to initiate a data acquisition and buffering process.
    headerRequestHeaderCommon request header.GetStatusGetStatusRequestGetStatusResponseQuery the status of a data acquisition.
    metadataAssociatedMetadataMetadata to store.GetServiceInfoGetServiceInfoRequestGetServiceInfoResponseGet information from a DAQ service; lists acquisition capabilities.
    data_idDataIdentifierData identifier of the metadata.CancelAcquisitionCancelAcquisitionRequestCancelAcquisitionResponseCancel an in-progress data acquisition.

    +

    -
    -

    StoreMetadataResponse

    +
    +
    +

    data_acquisition_store.proto

    +

    +
    +

    ActionIdQuery

    +

    A query parameter which filters the possible set of data identifiters to those +which contain the same action/group names matching any of the names in the +set of CaptureActionIds.

    @@ -4668,17 +4815,17 @@

    StoreMetadataResponse

    - - - + + + -
    headerResponseHeaderCommon response header.action_idsCaptureActionIdThe action ids to filter with.

    +

    -
    -

    TimeRangeQuery

    -

    A query parameter which filters the possible set of data identifiers to -those with timestamps within the specified range.

    +
    +

    DataQueryParams

    +

    The message containing the different query parameters which can be applied to +the ListData requests.

    @@ -4689,90 +4836,44 @@

    TimeRangeQuerygoogle.protobuf.Timestamp -

    + + + - - - + + + -
    Start of the time range to query.time_rangeTimeRangeQueryTime range to query.
    to_timestampgoogle.protobuf.TimestampEnd of the time range to query.action_idsActionIdQueryList of action ids to query.

    -
    +

    -
    -

    data_acquisition_store_service.proto

    -

    -
    -

    DataAcquisitionStoreService

    -

    The DataAcquisitionStoreService is used to store data (images, data, metadata) on the robot -in association with the DataIdentifiers specified by the DataAcquisitionService. Additionally, -requests can be made to the DataAcquisitionStoreService to identify different pieces of data or entire -capture actions which match query parameters, such as time ranges or action/group names.

    +
    +

    ListCaptureActionsRequest

    - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - + + + -
    Method NameRequest TypeResponse TypeFieldType Description
    ListCaptureActionsListCaptureActionsRequestListCaptureActionsResponseList all CaptureActionIds (which identify an entire AcquireData RPC's data captures) that match the query parameters provided in the request.
    ListStoredDataListStoredDataRequestListStoredDataResponseList data identifiers (which identify specific pieces of data from an action) for stored data that satisfy the query parameters in the request.
    StoreDataStoreDataRequestStoreDataResponseStore arbitrary data associated with a DataIdentifier.
    ListStoredImagesListStoredImagesRequestListStoredImagesResponseType-safe to images: list data identifiers (which identify specific images from an action) for stored images that satisfy the query parameters in the request.
    StoreImageStoreImageRequestStoreImageResponseType-safe to images: store image data associated with a DataIdentifier.
    ListStoredMetadataListStoredMetadataRequestListStoredMetadataResponseType-safe to JSON metadata: list data identifiers (which identify specific metadata from an action) for stored metadata that satisfy the query parameters in the request.headerRequestHeaderCommon request header.
    StoreMetadataStoreMetadataRequestStoreMetadataResponseType-safe to JSON metadata: store metadata associated with a DataIdentifier.queryDataQueryParamsQuery parameters for finding action ids.

    -
    +

    -
    -

    data_buffer.proto

    -

    -
    -

    DataBlob

    -

    Message-style data to add to the log.

    +
    +

    ListCaptureActionsResponse

    @@ -4783,31 +4884,20 @@

    DataBlobgoogle.protobuf.Timestamp -

    - - - - - - - - - - + + + - - - + + + -
    Timestamp of data in robot clock time. This is required.
    channelstringA general label for this blob. This is distinct from type_id, which identifies how the blob is to be parsed. In practice, this is often the same as the type_id.
    type_idstringA description of the data's content and its encoding. This is required. This should be sufficient for deciding how to deserialize the data. For example, this could be the full name of a protobuf message type.headerResponseHeaderCommon response header.
    databytesRaw data. For example, jpeg data or a serialized protobuf.action_idsCaptureActionIdList of action ids that satisfied the query parameters.

    +

    -
    -

    Event

    -

    This message contains event data for logging to the public timeline.

    +
    +

    ListStoredDataRequest

    @@ -4818,53 +4908,20 @@

    Event

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - + + + -
    typestringType of event, typically prefixed with a project or organization, e.g. "bosdyn:startup"
    descriptionstringEvent description. This is optional.
    sourcestringA description of the source of this event. May be the client name. - Not required to be unique. - Disambiguates the source of similar event types.
    idstringUnique identifier to link start and end messages for events with a duration. - Long running events may have separate messages at the start and end, in case the message for the end of the event is lost. - For events without a separate start and end message (in which case both start_time and end time should be specified), the 'id' field should not be set. - This id is not tracked internally by the service. It is only used to consume the event timeline. - To be effective, the id value should be generated randomly by the client.
    start_timegoogle.protobuf.TimestampStart and end times for the event: - Some events are instantaneous. For these, set start_timestamp and end_timestamp to the same value and send a single message (without an id). - Some events take time. At the onset, send a message with a unique id, the start time, and type. The end message should include all data from the start message, any additional data, and an end time. If you have the end message, you should not need the start message since it is a strict subset.
    end_timegoogle.protobuf.Timestamp
    levelEvent.LevelThe relative importance of the event.headerRequestHeaderCommon request header.
    parametersParameterOptional set of event parameters.queryDataQueryParamsQuery parameters for finding data.

    +

    -
    -

    OperatorComment

    -

    An operator comment to be added to the log. -These are notes especially intended to mark when logs should be preserved and reviewed -to ensure that robot hardware and/or software is working as intended.

    +
    +

    ListStoredDataResponse

    @@ -4875,20 +4932,20 @@

    OperatorCommentstring -

    + + + - - - + + + -
    String annotation message to add to the log.headerResponseHeaderCommon response header.
    timestampgoogle.protobuf.TimestampThe timestamp of the annotation. This must be in robot time. If this is not specified, this will default to the time the server received the message.data_idsDataIdentifierList of data identifiers that satisfied the query parameters.

    +

    -
    -

    RecordDataBlobsRequest

    +
    +

    ListStoredImagesRequest

    @@ -4904,20 +4961,15 @@

    RecordDataBlobsRequestCommon request header.

    - - - - - - - - + + + -
    blob_dataDataBlobThe data blobs to be logged.
    syncboolWhen set, the data blob is committed to the log synchronously. The RPC does not return until the data is written.queryDataQueryParamsQuery parameters for finding images.

    +

    -
    -

    RecordDataBlobsResponse

    +
    +

    ListStoredImagesResponse

    @@ -4933,45 +4985,15 @@

    RecordDataBlobsResponseCommon response header.

    - - - - - -
    errorsRecordDataBlobsResponse.ErrorErrors which occurred when logging data blobs.

    -
    -
    -

    RecordDataBlobsResponse.Error

    -

    DataBlob recording error.

    - - - - - - - - - - - - - - - - - - - - - - - + + + -
    FieldTypeDescription
    typeRecordDataBlobsResponse.Error.TypeThe type of error: if it was caused by the client or the service.
    messagestringAn error message.
    indexuint32The index to identify the data being stored.data_idsDataIdentifierList of image data identifiers that satisfied the query parameters.

    +

    -
    -

    RecordEventsRequest

    +
    +

    ListStoredMetadataRequest

    @@ -4987,15 +5009,15 @@

    RecordEventsRequestEvent -

    + + + -
    The events to be logged.queryDataQueryParamsQuery parameters for finding metadata.

    +

    -
    -

    RecordEventsResponse

    +
    +

    ListStoredMetadataResponse

    @@ -5011,16 +5033,15 @@

    RecordEventsResponseCommon response header.

    - - - + + + -
    errorsRecordEventsResponse.ErrorErrors which occurred when logging events.data_idsDataIdentifierList of metadata data identifiers that satisfied the query parameters.

    +

    -
    -

    RecordEventsResponse.Error

    -

    Event recording error.

    +
    +

    StoreDataRequest

    @@ -5031,25 +5052,30 @@

    RecordEventsResponse.ErrorRecordEventsResponse.Error.Type -

    + + + - - - + + + - - - + + + + + + + + -
    The type of error: if it was caused by the client, the service, or something else.headerRequestHeaderCommon request header.
    messagestringAn error message.databytesData to store.
    indexuint32The index to identify the data being stored.data_idDataIdentifierData identifier of the data.
    file_extensionstringFile extension to use when writing the data to file.

    +

    -
    -

    RecordOperatorCommentsRequest

    +
    +

    StoreDataResponse

    @@ -5061,19 +5087,14 @@

    RecordOperatorCommentsRequestRequestHeader -

    - - - - - + + -
    Common request header.
    operator_commentsOperatorCommentThe operator comments to be logged.ResponseHeaderCommon response header.

    +

    -
    -

    RecordOperatorCommentsResponse

    +
    +

    StoreImageRequest

    @@ -5085,20 +5106,24 @@

    RecordOperatorCommentsResponseResponseHeader -

    + + - - - + + + + + + + + -
    Common response header.RequestHeaderCommon request header.
    errorsRecordOperatorCommentsResponse.ErrorErrors which occurred when logging operator comments.imageImageCaptureImage to store.
    data_idDataIdentifierData identifier of the image.

    +

    -
    -

    RecordOperatorCommentsResponse.Error

    -

    Operator comment recording error.

    +
    +

    StoreImageResponse

    @@ -5109,25 +5134,15 @@

    RecordOperatorCommentsResponse.ErrorRecordOperatorCommentsResponse.Error.Type -

    - - - - - - - - - - + + + -
    The type of error: if it was caused by the client or the service.
    messagestringAn error message.
    indexuint32The index to identify the data being stored.headerResponseHeaderCommon response header.

    +

    -
    -

    RecordSignalTicksRequest

    +
    +

    StoreMetadataRequest

    @@ -5143,15 +5158,20 @@

    RecordSignalTicksRequestSignalTick -

    + + + + + + + + -
    The signals data to be logged.metadataAssociatedMetadataMetadata to store.
    data_idDataIdentifierData identifier of the metadata.

    +

    -
    -

    RecordSignalTicksResponse

    +
    +

    StoreMetadataResponse

    @@ -5166,17 +5186,13 @@

    RecordSignalTicksResponseResponseHeader

    - - - - - -
    Common response header.
    errorsRecordSignalTicksResponse.ErrorErrors which occurred when logging signal ticks.

    +

    -
    -

    RecordSignalTicksResponse.Error

    -

    Signal tick recording error.

    +
    +

    TimeRangeQuery

    +

    A query parameter which filters the possible set of data identifiers to +those with timestamps within the specified range.

    @@ -5187,49 +5203,90 @@

    RecordSignalTicksResponse.ErrorRecordSignalTicksResponse.Error.Type -

    - - - - - + + + - - - + + + -
    The type of error: if it was caused by the client, the service, or something else.
    messagestringAn error message.from_timestampgoogle.protobuf.TimestampStart of the time range to query.
    indexuint32The index to identify the data being stored.to_timestampgoogle.protobuf.TimestampEnd of the time range to query.

    +

    -
    -

    RecordTextMessagesRequest

    +
    +
    +

    data_acquisition_store_service.proto

    +

    +
    +

    DataAcquisitionStoreService

    +

    The DataAcquisitionStoreService is used to store data (images, data, metadata) on the robot +in association with the DataIdentifiers specified by the DataAcquisitionService. Additionally, +requests can be made to the DataAcquisitionStoreService to identify different pieces of data or entire +capture actions which match query parameters, such as time ranges or action/group names.

    - - + + + - - - + + + + - - - + + + + - -
    FieldTypeMethod NameRequest TypeResponse Type Description
    headerRequestHeaderCommon request header.ListCaptureActionsListCaptureActionsRequestListCaptureActionsResponseList all CaptureActionIds (which identify an entire AcquireData RPC's data captures) that match the query parameters provided in the request.
    text_messagesTextMessageThe text messages to be logged.ListStoredDataListStoredDataRequestListStoredDataResponseList data identifiers (which identify specific pieces of data from an action) for stored data that satisfy the query parameters in the request.

    -
    -
    -

    RecordTextMessagesResponse

    + +StoreData +StoreDataRequest +StoreDataResponse +Store arbitrary data associated with a DataIdentifier. + + +ListStoredImages +ListStoredImagesRequest +ListStoredImagesResponse +Type-safe to images: list data identifiers (which identify specific images from an action) for stored images that satisfy the query parameters in the request. + + +StoreImage +StoreImageRequest +StoreImageResponse +Type-safe to images: store image data associated with a DataIdentifier. + + +ListStoredMetadata +ListStoredMetadataRequest +ListStoredMetadataResponse +Type-safe to JSON metadata: list data identifiers (which identify specific metadata from an action) for stored metadata that satisfy the query parameters in the request. + + +StoreMetadata +StoreMetadataRequest +StoreMetadataResponse +Type-safe to JSON metadata: store metadata associated with a DataIdentifier. + + +

    +
    +
    +
    +

    data_buffer.proto

    +

    +
    +

    DataBlob

    +

    Message-style data to add to the log.

    @@ -5240,21 +5297,31 @@

    RecordTextMessagesResponseResponseHeader -

    + + + - - - + + + + + + + + + + + + + -
    Common response header.timestampgoogle.protobuf.TimestampTimestamp of data in robot clock time. This is required.
    errorsRecordTextMessagesResponse.ErrorErrors which occurred when logging text message data.channelstringA general label for this blob. This is distinct from type_id, which identifies how the blob is to be parsed. In practice, this is often the same as the type_id.
    type_idstringA description of the data's content and its encoding. This is required. This should be sufficient for deciding how to deserialize the data. For example, this could be the full name of a protobuf message type.
    databytesRaw data. For example, jpeg data or a serialized protobuf.

    +

    -
    -

    RecordTextMessagesResponse.Error

    -

    Text message recording error.

    +
    +

    Event

    +

    This message contains event data for logging to the public timeline.

    @@ -5266,24 +5333,81 @@

    RecordTextMessagesResponse.ErrorRecordTextMessagesResponse.Error.Type -

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    The type of error: if it was caused by the client or the service.stringType of event, typically prefixed with a project or organization, e.g. "bosdyn:startup"
    descriptionstringEvent description. This is optional.
    sourcestringA description of the source of this event. May be the client name. - Not required to be unique. - Disambiguates the source of similar event types.
    idstringUnique identifier to link start and end messages for events with a duration. - Long running events may have separate messages at the start and end, in case the message for the end of the event is lost. - For events without a separate start and end message (in which case both start_time and end time should be specified), the 'id' field should not be set. - This id is not tracked internally by the service. It is only used to consume the event timeline. - To be effective, the id value should be generated randomly by the client.
    start_timegoogle.protobuf.TimestampStart and end times for the event: - Some events are instantaneous. For these, set start_timestamp and end_timestamp to the same value and send a single message (without an id). - Some events take time. At the onset, send a message with a unique id, the start time, and type. The end message should include all data from the start message, any additional data, and an end time. If you have the end message, you should not need the start message since it is a strict subset.
    end_timegoogle.protobuf.Timestamp
    levelEvent.LevelThe relative importance of the event.
    parametersParameterOptional set of event parameters.
    log_preserve_hintEvent.LogPreserveHintOptionally request that the robot try to preserve data near this time for a service log.

    +
    +
    +

    OperatorComment

    +

    An operator comment to be added to the log. +These are notes especially intended to mark when logs should be preserved and reviewed +to ensure that robot hardware and/or software is working as intended.

    + + + + + + + + - + - - - + + + -
    FieldTypeDescription
    message stringAn error message.String annotation message to add to the log.
    indexuint32The index to identify the data being stored.timestampgoogle.protobuf.TimestampThe timestamp of the annotation. This must be in robot time. If this is not specified, this will default to the time the server received the message.

    +

    -
    -

    RegisterSignalSchemaRequest

    +
    +

    RecordDataBlobsRequest

    @@ -5296,18 +5420,23 @@

    RegisterSignalSchemaRequestRequestHeader -

    + - - - + + + + + + + + -
    Common request/response header.Common request header.
    schemaSignalSchemaDefines a schema for interpreting SignalTick data containing packed signals-type data.blob_dataDataBlobThe data blobs to be logged.
    syncboolWhen set, the data blob is committed to the log synchronously. The RPC does not return until the data is written.

    +

    -
    -

    RegisterSignalSchemaResponse

    +
    +

    RecordDataBlobsResponse

    @@ -5320,19 +5449,19 @@

    RegisterSignalSchemaResponseResponseHeader -

    + - - - + + + -
    Common request/response header.Common response header.
    schema_iduint64Server returns a unique ID based on the client ID and schema definition. Always greater than zero.errorsRecordDataBlobsResponse.ErrorErrors which occurred when logging data blobs.

    +

    -
    -

    SignalSchema

    -

    A description of a set of signals-style variables to log together as timestamped samples.

    +
    +

    RecordDataBlobsResponse.Error

    +

    DataBlob recording error.

    @@ -5343,21 +5472,25 @@

    SignalSchemaSignalSchema.Variable -

    + + + - + - + + + + + + -
    A SignalTick using this schema contains the values of this ordered list of variables.typeRecordDataBlobsResponse.Error.TypeThe type of error: if it was caused by the client or the service.
    schema_namemessage stringThe name of the schema.An error message.
    indexuint32The index to identify the data being stored.

    +

    -
    -

    SignalSchema.Variable

    -

    A variable of signals-style data, which will be sampled in time.

    +
    +

    RecordEventsRequest

    @@ -5368,25 +5501,20 @@

    SignalSchema.Variable

    - - - - - - - - + + + - - - + + + -
    namestringThe name of the variable.
    typeSignalSchema.Variable.TypeThe type of the data.headerRequestHeaderCommon request header.
    is_timeboolZero or one variable in 'vars' may be specified as a time variable. A time variable must have type TYPE_FLOAT64.eventsEventThe events to be logged.

    +

    -
    -

    SignalSchemaId

    +
    +

    RecordEventsResponse

    @@ -5397,21 +5525,21 @@

    SignalSchemaIduint64 -

    + + + - - - + + + -
    {schema, id} pairheaderResponseHeaderCommon response header.
    schemaSignalSchemaerrorsRecordEventsResponse.ErrorErrors which occurred when logging events.

    +

    -
    -

    SignalTick

    -

    A timestamped set of signals variable values.

    +
    +

    RecordEventsResponse.Error

    +

    Event recording error.

    @@ -5422,42 +5550,49 @@

    SignalTickint64 -

    + + + - - - + + + - - - + + + + +
    Successive ticks should have successive sequence_id's. The robot uses this to determine if a tick was somehow lost.typeRecordEventsResponse.Error.TypeThe type of error: if it was caused by the client, the service, or something else.
    timestampgoogle.protobuf.TimestampTimestamp at which the variable values were sampled.messagestringAn error message.
    sourcestringThe client name. This may be used to segregate data for the same variables to different parts of the buffer.indexuint32The index to identify the data being stored.

    +
    +
    +

    RecordOperatorCommentsRequest

    + + - - - + + + + + - - - + + + - - - + + + -
    schema_iduint64This specifies the SignalSchema to be used in interpreting theFieldTypeDescription
    encodingSignalTick.EncodingFormat describing how the data bytes array is encoded.headerRequestHeaderCommon request header.
    databytesThe encoded data representing a tick of multiple values of signal-styles data.operator_commentsOperatorCommentThe operator comments to be logged.

    +

    -
    -

    TextMessage

    -

    A text message to add to the log. -These could be internal text-log messages from a client for use in debugging, for example.

    +
    +

    RecordOperatorCommentsResponse

    @@ -5468,876 +5603,868 @@

    TextMessagestring -

    - - - - - + + + - - - + + + + +
    String annotation message.
    timestampgoogle.protobuf.TimestampThe timestamp of the annotation. This must be in robot time. If this is not specified, this will default to the time the server received the message.headerResponseHeaderCommon response header.
    sourcestringThe client name. This may be used to segregate data for the same variables to different parts of the buffer.errorsRecordOperatorCommentsResponse.ErrorErrors which occurred when logging operator comments.

    +
    +
    +

    RecordOperatorCommentsResponse.Error

    +

    Operator comment recording error.

    + + - - - + + + + + - - - + + + - + - + - - - + + + -
    levelTextMessage.LevelThe relative importance of the message.FieldTypeDescription
    tagstringOptional tag to identify from what code/module this message originated from.typeRecordOperatorCommentsResponse.Error.TypeThe type of error: if it was caused by the client or the service.
    filenamemessage stringOptional source file name originating the log message.An error message.
    line_numberint32Optional source file line number originating the log message.indexuint32The index to identify the data being stored.

    +

    -
    -

    Event.Level

    -

    Level, or similarly “visibility,” “importance,” or “weight” of event.

    -
      -
    • Higher level events will increase the visibility on the event timeline, relative to other -events.

    • -
    • In general, higher level events should be more consequential with respect to the robot -operation on a per-occurence basis.

    • -
    • Lower level events should be less consequential on a per occurence basis.

    • -
    • Non-critical events may be one of LOW, MEDIUM, or HIGH. UNSET is logically equivalent to -LOW level.

    • -
    • Critical events may be either mission or system critical.

    • -
    • System-critical is quasi-reserved for internal robot use, and is used to identify events -that directly affect robot status or capability, such as the onset of a critical fault or -start of an enabling capability.

    • -
    • Mission-critical is quasi-reserved client use, and is intended for events that directly -affect the ability of the robot to “do what the user wants,” such as the onset of a -service fault or start of an enabling capability.

    • -
    +
    +

    RecordSignalTicksRequest

    - - + + - - - - - - - - + + + - - - + + + + +
    NameNumberFieldType Description
    LEVEL_UNSET0Non-critical events
    LEVEL_LOW1headerRequestHeaderCommon request header.
    LEVEL_MEDIUM2tick_dataSignalTickThe signals data to be logged.

    +
    +
    +

    RecordSignalTicksResponse

    + + - - - + + + + + - - - + + + - - - + + + -
    LEVEL_HIGH3FieldTypeDescription
    LEVEL_MISSION_CRITICAL4Critical eventsheaderResponseHeaderCommon response header.
    LEVEL_SYSTEM_CRITICAL5errorsRecordSignalTicksResponse.ErrorErrors which occurred when logging signal ticks.

    +

    -
    -

    RecordDataBlobsResponse.Error.Type

    +
    +

    RecordSignalTicksResponse.Error

    +

    Signal tick recording error.

    - - + + - - - + + + - - - + + + - - - + + + -
    NameNumberFieldType Description
    NONE0typeRecordSignalTicksResponse.Error.TypeThe type of error: if it was caused by the client, the service, or something else.
    CLIENT_ERROR1messagestringAn error message.
    SERVER_ERROR2indexuint32The index to identify the data being stored.

    +

    -
    -

    RecordEventsResponse.Error.Type

    +
    +

    RecordTextMessagesRequest

    - - + + - - - - - - - - + + + - - - + + + -
    NameNumberFieldType Description
    NONE0
    CLIENT_ERROR1headerRequestHeaderCommon request header.
    SERVER_ERROR2text_messagesTextMessageThe text messages to be logged.

    +

    -
    -

    RecordOperatorCommentsResponse.Error.Type

    +
    +

    RecordTextMessagesResponse

    - - + + - - - - - - - - + + + - - - + + + -
    NameNumberFieldType Description
    NONE0
    CLIENT_ERROR1headerResponseHeaderCommon response header.
    SERVER_ERROR2errorsRecordTextMessagesResponse.ErrorErrors which occurred when logging text message data.

    +

    -
    -

    RecordSignalTicksResponse.Error.Type

    +
    +

    RecordTextMessagesResponse.Error

    +

    Text message recording error.

    - - + + - - - - - - - - + + + - - - + + + - - - + + + -
    NameNumberFieldType Description
    NONE0
    CLIENT_ERROR1typeRecordTextMessagesResponse.Error.TypeThe type of error: if it was caused by the client or the service.
    SERVER_ERROR2messagestringAn error message.
    INVALID_SCHEMA_ID3indexuint32The index to identify the data being stored.

    +

    -
    -

    RecordTextMessagesResponse.Error.Type

    +
    +

    RegisterSignalSchemaRequest

    - - + + - - - - - - - - + + + - - - + + + -
    NameNumberFieldType Description
    NONE0
    CLIENT_ERROR1headerRequestHeaderCommon request/response header.
    SERVER_ERROR2schemaSignalSchemaDefines a schema for interpreting SignalTick data containing packed signals-type data.

    +

    -
    -

    SignalSchema.Variable.Type

    +
    +

    RegisterSignalSchemaResponse

    - - + + - - - - - - - - - - - - - + + + - - - + + + + +
    NameNumberFieldType Description
    TYPE_UNKNOWN0
    TYPE_INT81
    TYPE_INT162headerResponseHeaderCommon request/response header.
    TYPE_INT323schema_iduint64Server returns a unique ID based on the client ID and schema definition. Always greater than zero.

    +
    +
    +

    SignalSchema

    +

    A description of a set of signals-style variables to log together as timestamped samples.

    + + - - - + + + + + - - - + + + - - - + + + + +
    TYPE_INT644FieldTypeDescription
    TYPE_UINT85varsSignalSchema.VariableA SignalTick using this schema contains the values of this ordered list of variables.
    TYPE_UINT166schema_namestringThe name of the schema.

    +
    +
    +

    SignalSchema.Variable

    +

    A variable of signals-style data, which will be sampled in time.

    + + - - - + + + + + - - - + + + - - - + + + - - - + + + -
    TYPE_UINT327FieldTypeDescription
    TYPE_UINT648namestringThe name of the variable.
    TYPE_FLOAT329typeSignalSchema.Variable.TypeThe type of the data.
    TYPE_FLOAT6410is_timeboolZero or one variable in 'vars' may be specified as a time variable. A time variable must have type TYPE_FLOAT64.

    +

    -
    -

    SignalTick.Encoding

    +
    +

    SignalSchemaId

    - - + + - - - + + + - - - + + + -
    NameNumberFieldType Description
    ENCODING_UNKNOWN0schema_iduint64{schema, id} pair
    ENCODING_RAW1Bytes array is a concatination of little-endian machine representations of the variables from the SignalSchema, in order listed in that schema.schemaSignalSchema

    +

    -
    -

    TextMessage.Level

    +
    +

    SignalTick

    +

    A timestamped set of signals variable values.

    - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + + + + + + -
    NameNumberFieldType Description
    LEVEL_UNKNOWN0Invalid, do not use.sequence_idint64Successive ticks should have successive sequence_id's. The robot uses this to determine if a tick was somehow lost.
    LEVEL_DEBUG1Events likely of interest only in a debugging context.timestampgoogle.protobuf.TimestampTimestamp at which the variable values were sampled.
    LEVEL_INFO2Informational message during normal operation.sourcestringThe client name. This may be used to segregate data for the same variables to different parts of the buffer.
    LEVEL_WARN3Information about an unexpected but recoverable condition.schema_iduint64This specifies the SignalSchema to be used in interpreting the
    LEVEL_ERROR4Information about an operation which did not succeed.encodingSignalTick.EncodingFormat describing how the data bytes array is encoded.
    databytesThe encoded data representing a tick of multiple values of signal-styles data.

    -
    +

    -
    -

    data_buffer_service.proto

    -

    DataBufferService allows adding information to the robot’s log files.

    -

    -
    -

    DataBufferService

    -

    This service is a mechanism for adding information to the robot’s log files.

    +
    +

    TextMessage

    +

    A text message to add to the log. +These could be internal text-log messages from a client for use in debugging, for example.

    - - - + + - - - - + + + - - - - + + + - - - - + + + - - - - + + + - - - - + + + - - - - + + + + + + + + -
    Method NameRequest TypeResponse TypeFieldType Description
    RecordTextMessagesRecordTextMessagesRequestRecordTextMessagesResponseAdd text messages to the log.messagestringString annotation message.
    RecordOperatorCommentsRecordOperatorCommentsRequestRecordOperatorCommentsResponseAdd a set of operator messages to the log.timestampgoogle.protobuf.TimestampThe timestamp of the annotation. This must be in robot time. If this is not specified, this will default to the time the server received the message.
    RecordDataBlobsRecordDataBlobsRequestRecordDataBlobsResponseAdd message-style data to the log.sourcestringThe client name. This may be used to segregate data for the same variables to different parts of the buffer.
    RecordEventsRecordEventsRequestRecordEventsResponseAdd event data to the log.levelTextMessage.LevelThe relative importance of the message.
    RegisterSignalSchemaRegisterSignalSchemaRequestRegisterSignalSchemaResponseRegister a log tick schema, allowing client to later log tick data.tagstringOptional tag to identify from what code/module this message originated from.
    RecordSignalTicksRecordSignalTicksRequestRecordSignalTicksResponseAdd signal data for registered signal schema to the log.filenamestringOptional source file name originating the log message.
    line_numberint32Optional source file line number originating the log message.

    -
    +

    -
    -

    data_chunk.proto

    -

    -
    -

    DataChunk

    -

    Represents a chunk of (possibly serialized) data. -Chunks will be concatenated together to produce a datagram. -This is to avoid size limit restrictions in grpc implementations.

    +
    +

    Event.Level

    +

    Level, or similarly “visibility,” “importance,” or “weight” of event.

    +
      +
    • Higher level events will increase the visibility on the event timeline, relative to other +events.

    • +
    • In general, higher level events should be more consequential with respect to the robot +operation on a per-occurence basis.

    • +
    • Lower level events should be less consequential on a per occurence basis.

    • +
    • Non-critical events may be one of LOW, MEDIUM, or HIGH. UNSET is logically equivalent to +LOW level.

    • +
    • Critical events may be either mission or system critical.

    • +
    • System-critical is quasi-reserved for internal robot use, and is used to identify events +that directly affect robot status or capability, such as the onset of a critical fault or +start of an enabling capability.

    • +
    • Mission-critical is quasi-reserved client use, and is intended for events that directly +affect the ability of the robot to “do what the user wants,” such as the onset of a +service fault or start of an enabling capability.

    • +
    - - + + - - - + + + - - - + + + - -
    FieldTypeNameNumber Description
    total_sizeuint64The total size in bytes of the datagram that this chunk is a part of.LEVEL_UNSET0Non-critical events
    databytesBytes in this data chunk. Bytes are sent sequentially.LEVEL_LOW1

    -
    -
    -
    -

    data_index.proto

    -

    -
    -

    BlobPage

    -

    A set of blob messages of a given channel/msgtype within a given data page.

    - - - - - + + + - - - - + + - - + + + + + + + -
    FieldTypeDescriptionLEVEL_MEDIUM2
    specBlobSpecLEVEL_HIGH3
    pagePageInfoLEVEL_MISSION_CRITICAL4Critical events
    LEVEL_SYSTEM_CRITICAL5

    +

    -
    -

    BlobPages

    -

    A set of pages of data which contain specified Blob messages from the data-buffer.

    +
    +

    Event.LogPreserveHint

    +

    LogPreserveHint may encode a hint to the robot’s logging system for whether to preserve +internal log data near the time of this event. This could be useful in saving data +to be used in a service log to send to Boston Dynamics.

    - - + + - - - + + + - - - + + + + + + + + -
    FieldTypeNameNumber Description
    time_rangeTimeRangeLOG_PRESERVE_HINT_UNSET0If this this is unset, it is equivalent to LOG_PRESERVE_HINT_NORMAL.
    pagesBlobPageLOG_PRESERVE_HINT_NORMAL1Do not change the robot's default log data preservation behavior in response to this event.
    LOG_PRESERVE_HINT_PRESERVE2Request that the robot try to preserve data near the time of this event. Log space on the robot is limited, so this does not guarentee that the data will be preserved.

    +

    -
    -

    BlobSpec

    -

    Specification for selecting of blob messages.

    +
    +

    RecordDataBlobsResponse.Error.Type

    - - + + - - + + - - + + - - + + -
    FieldTypeNameNumber Description
    sourcestringNONE0
    message_typestringCLIENT_ERROR1
    channelstringSERVER_ERROR2

    +

    -
    -

    DataBufferStatus

    +
    +

    RecordEventsResponse.Error.Type

    - - + + - - - - - - - - - - - - + + - - + + - - + + -
    FieldTypeNameNumber Description
    num_data_buffer_pagesint64
    data_buffer_total_bytesint64
    num_commentsint64NONE0
    num_eventsint64CLIENT_ERROR1
    blob_specsBlobSpecSERVER_ERROR2

    +

    -
    -

    DataIndex

    -

    Description of data matching a given DataQuery.

    +
    +

    RecordOperatorCommentsResponse.Error.Type

    - - + + - - - - - - - - - - - - + + - - + + - - + + -
    FieldTypeNameNumber Description
    time_rangeTimeRange
    blobsBlobPages
    text_messagesPagesAndTimestampNONE0
    eventsPagesAndTimestampCLIENT_ERROR1
    commentsPagesAndTimestampSERVER_ERROR2

    +

    -
    -

    DataQuery

    -

    A query for pages containing the desired data.

    +
    +

    RecordSignalTicksResponse.Error.Type

    - - + + - - - - - - - - + + + - - - + + + - - - + + + - - - + + + -
    FieldTypeNameNumber Description
    time_rangeTimeRangeTimespan for data we want to query
    blobsBlobSpecRequest for pages containing different kinds of data.NONE0
    text_messagesboolreturn pages of text-messages during the specified timespanCLIENT_ERROR1
    eventsboolreturn pages of eventsSERVER_ERROR2
    commentsboolreturn pages of operator comments during the specified timespanINVALID_SCHEMA_ID3

    +

    -
    -

    DeleteDataPagesRequest

    -

    GRPC request to delete pages. Both time_range and page_ids can be set.

    +
    +

    RecordTextMessagesResponse.Error.Type

    - - + + - - + + - - - + + + - - - + + + -
    FieldTypeNameNumber Description
    headerRequestHeaderNONE0
    time_rangeTimeRangeDelete all pages in this time rangeCLIENT_ERROR1
    page_idsstringDelete all pages with matching idsSERVER_ERROR2

    +

    -
    -

    DeleteDataPagesResponse

    +
    +

    SignalSchema.Variable.Type

    - - + + - - + + - - + + - - + + - -
    FieldTypeNameNumber Description
    headerResponseHeaderTYPE_UNKNOWN0
    bytes_deletedint64TYPE_INT81
    statusDeletePageStatusTYPE_INT162

    -
    -
    -

    DeletePageStatus

    - - - - - + + + - - - - + + - - + + - -
    FieldTypeDescriptionTYPE_INT323
    page_idstringTYPE_INT644
    statusDeletePageStatus.StatusTYPE_UINT85

    -
    -
    -

    EventSpec

    -

    Specification for selecting Events.

    - - - - - + + + - - - - + + - - + + - - + + + + + + + -
    FieldTypeDescriptionTYPE_UINT166
    sourcestringTYPE_UINT327
    typestringTYPE_UINT648
    levelgoogle.protobuf.Int32ValueTYPE_FLOAT329
    TYPE_FLOAT6410

    +

    -
    -

    EventsComments

    -

    Requested Events and/or OperatorComments.

    +
    +

    SignalTick.Encoding

    - - + + - - - - - - - + + - - - + + + -
    FieldTypeNameNumber Description
    time_rangeTimeRangeTimespan for data
    eventsEventENCODING_UNKNOWN0
    operator_commentsOperatorCommentENCODING_RAW1Bytes array is a concatination of little-endian machine representations of the variables from the SignalSchema, in order listed in that schema.

    +

    -
    -

    EventsCommentsSpec

    -

    A request for Events and/or OperatorComments over a given time range.

    +
    +

    TextMessage.Level

    - - + + - - - + + + - - - + + + - - - + + + + + + + + + + + + + -
    FieldTypeNameNumber Description
    time_rangeTimeRangeTimespan for data we want to queryLEVEL_UNKNOWN0Invalid, do not use.
    eventsEventSpecReturn events which match the request.LEVEL_DEBUG1Events likely of interest only in a debugging context.
    commentsboolReturn operator comments which match the request.LEVEL_INFO2Informational message during normal operation.
    LEVEL_WARN3Information about an unexpected but recoverable condition.
    LEVEL_ERROR4Information about an operation which did not succeed.

    +

    -
    -

    GetDataBufferStatusRequest

    +
    +
    +

    data_buffer_service.proto

    +

    DataBufferService allows adding information to the robot’s log files.

    +

    +
    +

    DataBufferService

    +

    This service is a mechanism for adding information to the robot’s log files.

    - - + + + - - - + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + -
    FieldTypeMethod NameRequest TypeResponse Type Description
    headerRequestHeaderRecordTextMessagesRecordTextMessagesRequestRecordTextMessagesResponseAdd text messages to the log.
    get_blob_specsboolRecordOperatorCommentsRecordOperatorCommentsRequestRecordOperatorCommentsResponseAdd a set of operator messages to the log.
    RecordDataBlobsRecordDataBlobsRequestRecordDataBlobsResponseAdd message-style data to the log.
    RecordEventsRecordEventsRequestRecordEventsResponseAdd event data to the log.
    RegisterSignalSchemaRegisterSignalSchemaRequestRegisterSignalSchemaResponseRegister a log tick schema, allowing client to later log tick data.
    RecordSignalTicksRecordSignalTicksRequestRecordSignalTicksResponseAdd signal data for registered signal schema to the log.

    +

    -
    -

    GetDataBufferStatusResponse

    +
    +
    +

    data_chunk.proto

    +

    +
    +

    DataChunk

    +

    Represents a chunk of (possibly serialized) data. +Chunks will be concatenated together to produce a datagram. +This is to avoid size limit restrictions in grpc implementations.

    @@ -6348,21 +6475,25 @@

    GetDataBufferStatusResponseResponseHeader -

    + + + - - - + + + -
    total_sizeuint64The total size in bytes of the datagram that this chunk is a part of.
    data_buffer_statusDataBufferStatusdatabytesBytes in this data chunk. Bytes are sent sequentially.

    +

    -
    -

    GetDataIndexRequest

    -

    GRPC response with requested data index information.

    +
    +
    +

    data_index.proto

    +

    +
    +

    BlobPage

    +

    A set of blob messages of a given channel/msgtype within a given data page.

    @@ -6373,21 +6504,21 @@

    GetDataIndexRequestRequestHeader +

    + - - + + -
    specBlobSpec
    data_queryDataQuerypagePageInfo

    +

    -
    -

    GetDataIndexResponse

    -

    GRPC request for data index information.

    +
    +

    BlobPages

    +

    A set of pages of data which contain specified Blob messages from the data-buffer.

    @@ -6398,20 +6529,21 @@

    GetDataIndexResponse

    - - + + - - + + -
    headerResponseHeadertime_rangeTimeRange
    data_indexDataIndexpagesBlobPage

    +

    -
    -

    GetDataPagesRequest

    +
    +

    BlobSpec

    +

    Specification for selecting of blob messages.

    @@ -6422,45 +6554,30 @@

    GetDataPagesRequestRequestHeader -

    - - - - - + + + - -
    time_rangeTimeRangesourcestringIf set, require the message source to match this.

    -
    -
    -

    GetDataPagesResponse

    - - - - - + + + - - - - - + + + - - - + + + -
    FieldTypeDescriptionmessage_typestringIf set, require the message type to match this value.
    headerResponseHeaderchannelstringIf set, require the channel to match this value (or channel_glob, if set).
    pagesPageInfochannel_globstringOptionally require the channel to match a glob (or channel, if set).. For example, 'gps/*' will match all channels starting with 'gps/'.

    +

    -
    -

    GetEventsCommentsRequest

    -

    GRPC request for Events and OperatorComments.

    +
    +

    DataBufferStatus

    @@ -6471,46 +6588,36 @@

    GetEventsCommentsRequestRequestHeader +

    + - - + + - -
    num_data_buffer_pagesint64
    event_comment_requestEventsCommentsSpecdata_buffer_total_bytesint64

    -
    -
    -

    GetEventsCommentsResponse

    -

    GRPC response with requested Events and OperatorComments.

    - - - - - + + + - - - - + + - - + + -
    FieldTypeDescriptionnum_commentsint64
    headerResponseHeadernum_eventsint64
    events_commentsEventsCommentsblob_specsBlobSpec

    +

    -
    -

    GrpcPages

    -

    A set of pages of data which contain specied GRPC request and response messages.

    +
    +

    DataIndex

    +

    Description of data matching a given DataQuery.

    @@ -6526,43 +6633,31 @@

    GrpcPagesGrpcSpec +

    + - - + + - -
    blobsBlobPages
    pagesPageInfotext_messagesPagesAndTimestamp

    -
    -
    -

    GrpcSpec

    -

    Specification for selecting of GRPC logs.

    - - - - - + + + - - - - + + -
    FieldTypeDescriptioneventsPagesAndTimestamp
    service_namestringcommentsPagesAndTimestamp

    +

    -
    -

    PageInfo

    -

    A unit of data storage. -This may be a bddf data file. -Like a file, this data may be downloaded or deleted all together for example.

    +
    +

    DataQuery

    +

    A query for pages containing the desired data.

    @@ -6573,76 +6668,36 @@

    PageInfostring -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - + + + - + - + - + - - - - - - - - - - - + - + - + -
    Identifier unique to robot.
    pathstringRelative path to file, if file storage.
    sourcestringName of service/client which provided the data.
    time_range TimeRangeTime range of the relevant data in the page.
    num_ticksint64Number of time samples or blobs.
    total_bytesint64Total size of data in the page.
    formatPageInfo.PageFormatTimespan for data we want to query
    compressionPageInfo.CompressionblobsBlobSpecRequest for pages containing different kinds of data.
    is_opentext_messages boolTrue if data is still being written into this page, false if page is complete.return pages of text-messages during the specified timespan
    is_downloadedevents boolTrue if data is marked as having been downloaded.
    deleted_timestampgoogle.protobuf.TimestampIf this exists, the page was deleted from the robot at the specified time.
    download_started_timestampgoogle.protobuf.TimestampIf this exists, download from this page was started at the specified time.return pages of events
    request_preservecomments boolTrue if data has been requested to be preserved.return pages of operator comments during the specified timespan

    +

    -
    -

    PagesAndTimestamp

    -

    A set of pages and the associated time range they cover.

    +
    +

    DeleteDataPagesRequest

    +

    GRPC request to delete pages. Both time_range and page_ids can be set.

    @@ -6653,169 +6708,197 @@

    PagesAndTimestampRequestHeader +

    + + - + - - - + + + -
    time_range TimeRangeDelete all pages in this time range
    pagesPageInfopage_idsstringDelete all pages with matching ids

    +

    -
    -

    DeletePageStatus.Status

    +
    +

    DeleteDataPagesResponse

    - - + + - - - - - - - + + - - + + - - + + -
    NameNumberFieldType Description
    STATUS_UNKNOWN0
    STATUS_DELETED1headerResponseHeader
    STATUS_DELETION_FAILED2bytes_deletedint64
    STATUS_NOT_FOUND3statusDeletePageStatus

    +

    -
    -

    PageInfo.Compression

    +
    +

    DeletePageStatus

    - - + + - - - + + + - - - + + + + +
    NameNumberFieldType Description
    COMPRESSION_UNKNOWN0Not set -- do not use.page_idstring
    COMPRESSION_NONE1Data is not compressed.statusDeletePageStatus.Status

    +
    +
    +

    EventSpec

    +

    Specification for selecting Events.

    + + - - - + + + + + - - - + + + + + + + + + + + + + + + + + + -
    COMPRESSION_GZIP2Data uses gzip compression.FieldTypeDescription
    COMPRESSION_ZSTD3Data uses zstd compression.sourcestring
    typestring
    levelgoogle.protobuf.Int32Value
    log_preserve_hintEvent.LogPreserveHint

    +

    -
    -

    PageInfo.PageFormat

    +
    +

    EventsComments

    +

    Requested Events and/or OperatorComments.

    - - + + - - - + + + - - - + + + + + + + + -
    NameNumberFieldType Description
    FORMAT_UNKNOWN0Unset -- do not use.time_rangeTimeRangeTimespan for data
    FORMAT_BDDF_FILE1Data is stored in a .bddf fileeventsEvent
    operator_commentsOperatorComment

    -
    +

    -
    -

    data_service.proto

    -

    DataBufferService allows adding information to the robot’s log files.

    -

    -
    -

    DataService

    -

    The DataService is a mechanism for querying and managing data stored on robot.

    +
    +

    EventsCommentsSpec

    +

    A request for Events and/or OperatorComments over a given time range.

    - - - + + - - - - + + + - - - - + + + - - - - + + + + +
    Method NameRequest TypeResponse TypeFieldType Description
    GetDataIndexGetDataIndexRequestGetDataIndexResponseGet index of current data matching a given DataQuery.time_rangeTimeRangeTimespan for data we want to query
    GetEventsCommentsGetEventsCommentsRequestGetEventsCommentsResponseGet events and comments.eventsEventSpecReturn events which match the request.
    GetDataBufferStatusGetDataBufferStatusRequestGetDataBufferStatusResponseGet basic stats on data buffer storage.commentsboolReturn operator comments which match the request.

    +
    +
    +

    GetDataBufferStatusRequest

    + + - - - - + + + + + - - - - + + + + + + + + -
    GetDataPagesGetDataPagesRequestGetDataPagesResponseGet a list pf pages matching a given time rangeFieldTypeDescription
    DeleteDataPagesDeleteDataPagesRequestDeleteDataPagesResponseDelete a list of pages matching a given time range or page idsheaderRequestHeader
    get_blob_specsbool

    -
    +

    -
    -

    directory.proto

    -

    -
    -

    Endpoint

    -

    A message containing information that allows a client to identify a -given endpoint host using an ip and a port.

    +
    +

    GetDataBufferStatusResponse

    @@ -6826,21 +6909,21 @@

    Endpointstring -

    + + + - - - + + + -
    The IP address of the computer hosting this endpoint.headerResponseHeader
    portint32The port number on which the endpoint is provided.data_buffer_statusDataBufferStatus

    +

    -
    -

    GetServiceEntryRequest

    -

    The GetServiceEntry request message sends the service name to the robot.

    +
    +

    GetDataIndexRequest

    +

    GRPC response with requested data index information.

    @@ -6853,19 +6936,19 @@

    GetServiceEntryRequest

    - + - - - + + + -
    header RequestHeaderCommon request header.
    service_namestringThe unique user-friendly name of the service.data_queryDataQuery

    +

    -
    -

    GetServiceEntryResponse

    -

    The GetServiceEntry response message returns a ServiceEntry for the desired service name.

    +
    +

    GetDataIndexResponse

    +

    GRPC request for data index information.

    @@ -6878,24 +6961,18 @@

    GetServiceEntryResponse

    - - - - - - + - - - + + + -
    header ResponseHeaderCommon response Header.
    statusGetServiceEntryResponse.StatusCurrent status of the request.
    service_entryServiceEntryThe record for the discovered service. Only set if 'status' field == STATUS_OK.data_indexDataIndex

    +

    -
    -

    ListServiceEntriesRequest

    -

    The ListServiceEntries request message will ask the robot for all services.

    +
    +

    GetDataPagesRequest

    @@ -6908,15 +6985,18 @@

    ListServiceEntriesRequestRequestHeader -

    + + + + + + -
    Common request header.
    time_rangeTimeRange

    +

    -
    -

    ListServiceEntriesResponse

    -

    The ListServiceEntries response message returns all known services at the time the request -was recieved.

    +
    +

    GetDataPagesResponse

    @@ -6929,21 +7009,19 @@

    ListServiceEntriesResponseResponseHeader -

    + - - - + + + -
    Common response header.
    service_entriesServiceEntryThe resources managed by the LeaseService.pagesPageInfo

    +

    -
    -

    ServiceEntry

    -

    A message representing a discoverable service. By definition, all services -discoverable by this system are expected to be grpc “services” provided by -some server.

    +
    +

    GetEventsCommentsRequest

    +

    GRPC request for Events and OperatorComments.

    @@ -6954,85 +7032,46 @@

    ServiceEntrystring -

    + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + -
    The unique user-friendly name of this service.headerRequestHeader
    typestringThe type of this service. Usually identifies the underlying implementation. Does not have to be unique among all ServiceEntry objects.
    authoritystringInformation used to route to the desired Service. Can either be a full address (aService.spot.robot) or just a DNS label that will be automatically converted to an address (aService).
    last_updategoogle.protobuf.TimestampLast update time in robot timebase for this service record. This serves as the time of the last heartbeat to the robot.
    user_token_requiredboolIf 'user_token_required' field is true, any requests to this service must contain a user token for the machine. Requests without a user token will result in a 401. Most services will want to require a user_token, but ones like auth_service do not.
    permission_requiredstringIf 'permission_required' field is non-empty, any requests to this service must have the same string in the "per" claim of the user token.
    liveness_timeout_secsdoubleNumber of seconds to wait between heartbeats before assuming service in no longer live If unset (0) liveness checks will be disabled for this service.
    host_payload_guidstringThe GUID of the payload that this service was registered from. An empty string represents a service that was registered via a client using standard user credentials or internal to the robot. This value is set automatically based on the user token and cannot be set or updated via the API, so it should not be populated by the client at registration time.event_comment_requestEventsCommentsSpec

    +

    -
    -

    GetServiceEntryResponse.Status

    +
    +

    GetEventsCommentsResponse

    +

    GRPC response with requested Events and OperatorComments.

    - - + + - - - - - - - - + + + - - - + + + -
    NameNumberFieldType Description
    STATUS_UNKNOWN0UNKNOWN should never be used. An internal DirectoryService issue has happened if UNKNOWN is set.
    STATUS_OK1GetService was successful. The service_entry field is filled out.headerResponseHeader
    STATUS_NONEXISTENT_SERVICE2GetService failed because the requested service name does not exist.events_commentsEventsComments

    -
    +

    -
    -

    directory_registration.proto

    -

    -
    -

    RegisterServiceRequest

    -

    The RegisterService request message sends the service’s entry and endpoint to the robot’s directory. -This Request serves as a heartbeat to the Directory.

    +
    +

    GrpcPages

    +

    A set of pages of data which contain specied GRPC request and response messages.

    @@ -7043,26 +7082,26 @@

    RegisterServiceRequest

    - - - + + + - - - + + + - - - + + + -
    headerRequestHeaderCommon request header.time_rangeTimeRange
    endpointEndpointThe endpoint at which this service may be contacted.specGrpcSpec
    service_entryServiceEntryThe service to create. The name must not match any existing service.pagesPageInfo

    +

    -
    -

    RegisterServiceResponse

    -

    The RegisterService response message has information of whether the service was registered correctly.

    +
    +

    GrpcSpec

    +

    Specification for selecting of GRPC logs.

    @@ -7073,21 +7112,18 @@

    RegisterServiceResponse

    - - - - - - - - + + + -
    headerResponseHeaderCommon response Header.
    statusRegisterServiceResponse.StatusReturn status for the request.service_namestring

    +

    -
    -

    UnregisterServiceRequest

    -

    The UnregisterService request message will unregister a service based on name.

    +
    +

    PageInfo

    +

    A unit of data storage. +This may be a bddf data file. +Like a file, this data may be downloaded or deleted all together for example.

    @@ -7098,77 +7134,76 @@

    UnregisterServiceRequestRequestHeader -

    + + + - + - + - -
    Common request header.idstringIdentifier unique to robot.
    service_namepath stringThe unique user-friendly name of the service.Relative path to file, if file storage.

    -
    -
    -

    UnregisterServiceResponse

    -

    The UnregisterService response message has information of whether the service was unregistered.

    - - - - - + + + - - - - - + + + - - - + + + - -
    FieldTypeDescriptionsourcestringName of service/client which provided the data.
    headerResponseHeaderCommon response Header.time_rangeTimeRangeTime range of the relevant data in the page.
    statusUnregisterServiceResponse.StatusReturn status for the request.num_ticksint64Number of time samples or blobs.

    -
    -
    -

    UpdateServiceRequest

    -

    The UpdateService request message will update a service based on name to include the new endpoint and service entry. -This Request serves as a heartbeat to the Directory.

    - - - - - + + + - - - - - + + + - - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + -
    FieldTypeDescriptiontotal_bytesint64Total size of data in the page.
    headerRequestHeaderCommon request header.formatPageInfo.PageFormat
    endpointEndpointThe endpoint at which this service may be contacted.compressionPageInfo.Compression
    service_entryServiceEntryNew record for service. The name field is used as lookup key.is_openboolTrue if data is still being written into this page, false if page is complete.
    is_downloadedboolTrue if data is marked as having been downloaded.
    deleted_timestampgoogle.protobuf.TimestampIf this exists, the page was deleted from the robot at the specified time.
    download_started_timestampgoogle.protobuf.TimestampIf this exists, download from this page was started at the specified time.
    request_preserveboolTrue if data has been requested to be preserved.

    +

    -
    -

    UpdateServiceResponse

    -

    The UpdateService response message has information of whether the service was updated on robot.

    +
    +

    PagesAndTimestamp

    +

    A set of pages and the associated time range they cover.

    @@ -7179,20 +7214,20 @@

    UpdateServiceResponse

    - - - + + + - - - + + + -
    headerResponseHeaderCommon response Header.time_rangeTimeRange
    statusUpdateServiceResponse.StatusReturn status for the request.pagesPageInfo

    +

    -
    -

    RegisterServiceResponse.Status

    +
    +

    DeletePageStatus.Status

    @@ -7205,23 +7240,28 @@

    RegisterServiceResponse.Status

    +

    -
    -

    UnregisterServiceResponse.Status

    +
    +

    PageInfo.Compression

    @@ -7232,25 +7272,30 @@

    UnregisterServiceResponse.Status

    +

    -
    -

    UpdateServiceResponse.Status

    +
    +

    PageInfo.PageFormat

    @@ -7261,33 +7306,26 @@

    UpdateServiceResponse.Status

    +

    -
    -

    directory_registration_service.proto

    -

    -
    -

    DirectoryRegistrationService

    -

    DirectoryRegistrationService is a private class that lets services be -discovered by clients by adding them to a discovery database. Services -can live on robot, payload, or other accessible cloud-based locations. -Each service is responsible for registering itself with this service.

    +
    +

    data_service.proto

    +

    DataBufferService allows adding information to the robot’s log files.

    +

    +
    +

    DataService

    +

    The DataService is a mechanism for querying and managing data stored on robot.

    @@ -7299,65 +7337,71 @@

    DirectoryRegistrationServiceRegisterServiceRequest -

    - + + + + - - - - + + + + - - - - + + + + + + + + + + + + + + + + -
    RegisterServiceResponseCalled by a producer to register as a provider with the application. Returns the record for that provider. Requires unique name and correctly filled out service record in request.GetDataIndexGetDataIndexRequestGetDataIndexResponseGet index of current data matching a given DataQuery.
    UnregisterServiceUnregisterServiceRequestUnregisterServiceResponseCalled by a producer to remove its registration from the DirectoryManager.GetEventsCommentsGetEventsCommentsRequestGetEventsCommentsResponseGet events and comments.
    UpdateServiceUpdateServiceRequestUpdateServiceResponseUpdate the ServiceEntry for a producer on the server.GetDataBufferStatusGetDataBufferStatusRequestGetDataBufferStatusResponseGet basic stats on data buffer storage.
    GetDataPagesGetDataPagesRequestGetDataPagesResponseGet a list pf pages matching a given time range
    DeleteDataPagesDeleteDataPagesRequestDeleteDataPagesResponseDelete a list of pages matching a given time range or page ids

    +

    -
    -

    directory_service.proto

    -

    -
    -

    DirectoryService

    -

    DirectoryService lets clients discover which API services are available on a robot.

    +
    +

    directory.proto

    +

    +
    +

    Endpoint

    +

    A message containing information that allows a client to identify a +given endpoint host using an ip and a port.

    - - - + + - - - - + + + - - - - + + + -
    Method NameRequest TypeResponse TypeFieldType Description
    GetServiceEntryGetServiceEntryRequestGetServiceEntryResponseGet information about a specific service.host_ipstringThe IP address of the computer hosting this endpoint.
    ListServiceEntriesListServiceEntriesRequestListServiceEntriesResponseList all known services at time of call.portint32The port number on which the endpoint is provided, between 0 and 65535.

    -
    +

    -
    -

    docking/docking.proto

    -

    -
    -

    ConfigRange

    -

    The configuration of a range of dock ID’s

    +
    +

    GetServiceEntryRequest

    +

    The GetServiceEntry request message sends the service name to the robot.

    @@ -7368,26 +7412,21 @@

    ConfigRangeuint32 -

    - - - - - + + + - - - + + + -
    Starting ID
    id_enduint32Ending IDheaderRequestHeaderCommon request header.
    typeDockTypeType of dock for this rangeservice_namestringThe unique user-friendly name of the service.

    +

    -
    -

    DockState

    -

    Message describing the overall dock state of the robot, including power & comms connections.
    Not tied to any particular DockingCommand ID.
    Note: [*] indicates fields which are only valid if the status is DOCK_STATUS_DOCKED or DOCK_STATUS_DOCKING
    Note: [^] indicates fields which are only valid if the status is DOCK_STATUS_DOCKED \

    +
    +

    GetServiceEntryResponse

    +

    The GetServiceEntry response message returns a ServiceEntry for the desired service name.

    @@ -7398,31 +7437,26 @@

    DockStateDockState.DockedStatus -

    - - - - - + + + - - - + + + - - - + + + -
    Status of if the robot is on dock
    dock_typeDockType[*] Type of the dockheaderResponseHeaderCommon response Header.
    dock_iduint32[*] ID of the dockstatusGetServiceEntryResponse.StatusCurrent status of the request.
    power_statusDockState.LinkStatus[^] Status of power detection from the dockservice_entryServiceEntryThe record for the discovered service. Only set if 'status' field == STATUS_OK.

    +

    -
    -

    DockingCommandFeedbackRequest

    -

    Message to get the status of a previously issued DockingCommand

    +
    +

    ListServiceEntriesRequest

    +

    The ListServiceEntries request message will ask the robot for all services.

    @@ -7434,20 +7468,16 @@

    DockingCommandFeedbackRequestbosdyn.api.RequestHeader +

    - - - - - -
    RequestHeader Common request header.
    docking_command_iduint32Unique identifier of the command to get feedback for.

    +

    -
    -

    DockingCommandFeedbackResponse

    -

    Response to a DockingCommandFeedbackRequest for a particualar docking command ID

    +
    +

    ListServiceEntriesResponse

    +

    The ListServiceEntries response message returns all known services at the time the request +was recieved.

    @@ -7459,26 +7489,22 @@

    DockingCommandFeedbackResponsebosdyn.api.ResponseHeader +

    - - - - - - - - + + + -
    ResponseHeader Common response header.
    lease_use_resultbosdyn.api.LeaseUseResultDetails about how the lease was used (unset if unknown).
    statusDockingCommandFeedbackResponse.StatusCurrent feedback of specified command ID.service_entriesServiceEntryThe resources managed by the LeaseService.

    +

    -
    -

    DockingCommandRequest

    -

    Message to command the robot to dock.
    Note: If the robot is docked, you can undock the robot by issuing a command with -prep_pose_behavior=PREP_POSE_UNDOCK. If undocking, docking_station_id is not required.

    +
    +

    ServiceEntry

    +

    A message representing a discoverable service. By definition, all services +discoverable by this system are expected to be grpc “services” provided by +some server.

    @@ -7489,75 +7515,85 @@

    DockingCommandRequest

    - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + + + + + + + + + + + -
    headerbosdyn.api.RequestHeaderCommon request header.namestringThe unique user-friendly name of this service.
    leasebosdyn.api.LeaseThe Lease to show ownership of the robot.typestringThe type of this service. Usually identifies the underlying implementation. Does not have to be unique among all ServiceEntry objects.
    docking_station_iduint32ID of docking station to dock at. This is ignored if undocking the robot, the current dock is used.authoritystringInformation used to route to the desired Service. Can either be a full address (aService.spot.robot) or just a DNS label that will be automatically converted to an address (aService).
    clock_identifierstringIdentifier provided by the time sync service to verify time sync between robot and client.last_updategoogle.protobuf.TimestampLast update time in robot timebase for this service record. This serves as the time of the last heartbeat to the robot.
    end_timegoogle.protobuf.TimestampThe timestamp (in robot time) by which a command must finish executing. This is a required field and used to prevent runaway commands.user_token_requiredboolIf 'user_token_required' field is true, any requests to this service must contain a user token for the machine. Requests without a user token will result in a 401. Most services will want to require a user_token, but ones like auth_service do not.
    prep_pose_behaviorPrepPoseBehavior[Optional] Specify the prep pose behaviorpermission_requiredstringIf 'permission_required' field is non-empty, any requests to this service must have the same string in the "per" claim of the user token.
    liveness_timeout_secsdoubleNumber of seconds to wait between heartbeats before assuming service in no longer live If unset (0) liveness checks will be disabled for this service.
    host_payload_guidstringThe GUID of the payload that this service was registered from. An empty string represents a service that was registered via a client using standard user credentials or internal to the robot. This value is set automatically based on the user token and cannot be set or updated via the API, so it should not be populated by the client at registration time.

    +

    -
    -

    DockingCommandResponse

    -

    Response to a DockingCommandRequest

    +
    +

    GetServiceEntryResponse.Status

    - - + + - - - - - - - - + + + - - - + + + - - - + + + -
    FieldTypeNameNumber Description
    headerbosdyn.api.ResponseHeaderCommon response header.
    lease_use_resultbosdyn.api.LeaseUseResultDetails about how the lease was used.STATUS_UNKNOWN0UNKNOWN should never be used. An internal DirectoryService issue has happened if UNKNOWN is set.
    statusDockingCommandResponse.StatusResult of issued command.STATUS_OK1GetService was successful. The service_entry field is filled out.
    docking_command_iduint32Unique identifier for the command (if accepted, status=STATUS_OK).STATUS_NONEXISTENT_SERVICE2GetService failed because the requested service name does not exist.

    +

    -
    -

    GetDockingConfigRequest

    +
    +
    +

    directory_registration.proto

    +

    +
    +

    RegisterServiceRequest

    +

    The RegisterService request message sends the service’s entry and endpoint to the robot’s directory. +This Request serves as a heartbeat to the Directory.

    @@ -7569,14 +7605,25 @@

    GetDockingConfigRequest

    - + + + + + + + + + + + -
    headerbosdyn.api.RequestHeaderRequestHeader Common request header.
    endpointEndpointThe endpoint at which this service may be contacted.
    service_entryServiceEntryThe service to create. The name must not match any existing service.

    +

    -
    -

    GetDockingConfigResponse

    +
    +

    RegisterServiceResponse

    +

    The RegisterService response message has information of whether the service was registered correctly.

    @@ -7588,20 +7635,20 @@

    GetDockingConfigResponsebosdyn.api.ResponseHeader -

    + + - - - + + + -
    Common response header.ResponseHeaderCommon response Header.
    dock_configsConfigRangeA series of ConfigRange specifying details for dock ID numbers.statusRegisterServiceResponse.StatusReturn status for the request.

    +

    -
    -

    GetDockingStateRequest

    -

    Message to get the overall docking state

    +
    +

    UnregisterServiceRequest

    +

    The UnregisterService request message will unregister a service based on name.

    @@ -7613,15 +7660,20 @@

    GetDockingStateRequest

    - + + + + + + -
    headerbosdyn.api.RequestHeaderRequestHeader Common request header.
    service_namestringThe unique user-friendly name of the service.

    +

    -
    -

    GetDockingStateResponse

    -

    Response of a GetDockingStateRequest

    +
    +

    UnregisterServiceResponse

    +

    The UnregisterService response message has information of whether the service was unregistered.

    @@ -7633,53 +7685,75 @@

    GetDockingStateResponse

    - - + + - - - + + + -
    headerbosdyn.api.ResponseHeaderCommon response header.ResponseHeaderCommon response Header.
    dock_stateDockStatestatusUnregisterServiceResponse.StatusReturn status for the request.

    +

    -
    -

    DockState.DockedStatus

    +
    +

    UpdateServiceRequest

    +

    The UpdateService request message will update a service based on name to include the new endpoint and service entry. +This Request serves as a heartbeat to the Directory.

    - - + + - - - + + + - - - + + + - - - + + + + +
    NameNumberFieldType Description
    DOCK_STATUS_UNKNOWN0UnknownheaderRequestHeaderCommon request header.
    DOCK_STATUS_DOCKED1Robot is detected as on a dockendpointEndpointThe endpoint at which this service may be contacted.
    DOCK_STATUS_DOCKING2Robot is currently running a docking commandservice_entryServiceEntryNew record for service. The name field is used as lookup key.

    +
    +
    +

    UpdateServiceResponse

    +

    The UpdateService response message has information of whether the service was updated on robot.

    + + - - - + + + + + + + + + + + + + + + -
    DOCK_STATUS_UNDOCKED3Robot is not detected as on dockFieldTypeDescription
    headerResponseHeaderCommon response Header.
    statusUpdateServiceResponse.StatusReturn status for the request.

    +

    -
    -

    DockState.LinkStatus

    +
    +

    RegisterServiceResponse.Status

    @@ -7690,26 +7764,25 @@

    DockState.LinkStatus

    - + - + - + - + - + - + -
    LINK_STATUS_UNKNOWNSTATUS_UNKNOWN 0Unknown or Not applicableUNKNOWN should never be used. An internal DirectoryRegistrationService issue has happened if UNKNOWN is set.
    LINK_STATUS_CONNECTEDSTATUS_OK 1The link is detected as connectedSuccess. The new service record is available.
    LINK_STATUS_ERRORSTATUS_ALREADY_EXISTS 2The link could not be detectedRegisterService failed because a service with this name already exists.

    +

    -
    -

    DockType

    -

    Type of dock

    +
    +

    UnregisterServiceResponse.Status

    @@ -7720,25 +7793,25 @@

    DockType

    +

    -
    -

    DockingCommandFeedbackResponse.Status

    +
    +

    UpdateServiceResponse.Status

    @@ -7751,197 +7824,166 @@

    DockingCommandFeedbackResponse.Status

    + + +
    +

    directory_registration_service.proto

    +

    +
    +

    DirectoryRegistrationService

    +

    DirectoryRegistrationService is a private class that lets services be +discovered by clients by adding them to a discovery database. Services +can live on robot, payload, or other accessible cloud-based locations. +Each service is responsible for registering itself with this service.

    +

    + - - - + + + + + + - - - + + + + - - - + + + + - - - + + + + -
    STATUS_ERROR_COMMAND_TIMED_OUT6ERROR: End time has been reached.Method NameRequest TypeResponse TypeDescription
    STATUS_ERROR_NO_TIMESYNC7ERROR: No Timesync with system.RegisterServiceRegisterServiceRequestRegisterServiceResponseCalled by a producer to register as a provider with the application. Returns the record for that provider. Requires unique name and correctly filled out service record in request.
    STATUS_ERROR_TOO_DISTANT8ERROR: Provided end time too far in the future.UnregisterServiceUnregisterServiceRequestUnregisterServiceResponseCalled by a producer to remove its registration from the DirectoryManager.
    STATUS_ERROR_SYSTEM9ERROR: Internal system error during execution This error cannot be resolved by issuing a new DockingCommand Check the returned message for detailsUpdateServiceUpdateServiceRequestUpdateServiceResponseUpdate the ServiceEntry for a producer on the server.

    +

    -
    -

    DockingCommandResponse.Status

    +
    +
    +

    directory_service.proto

    +

    +
    +

    DirectoryService

    +

    DirectoryService lets clients discover which API services are available on a robot.

    - - + + + - - - - - - - - - - - - - - - - - - + + + + - - - + + + + -
    NameNumberMethod NameRequest TypeResponse Type Description
    STATUS_UNKNOWN0Status is not specified.
    STATUS_OK1Docking command accepted
    STATUS_ERROR_LEASE4ERROR: Lease rejected
    STATUS_ERROR_DOCK_NOT_FOUND5ERROR: Dock fiducial not found.GetServiceEntryGetServiceEntryRequestGetServiceEntryResponseGet information about a specific service.
    STATUS_ERROR_NOT_DOCKED6ERROR: Trying to undock while not dockedListServiceEntriesListServiceEntriesRequestListServiceEntriesResponseList all known services at time of call.

    +

    -
    -

    PrepPoseBehavior

    -

    Defines how and whether we use the “pre-docking” pose.

    +
    +
    +

    docking/docking.proto

    +

    +
    +

    ConfigRange

    +

    The configuration of a range of dock ID’s

    - - + + - - - - - - - - - - - - - + + + - - - + + + - - - + + + -
    NameNumberFieldType Description
    PREP_POSE_UNKNOWN0Default behavior, equivalent to PREP_POSE_USE_POSE.
    PREP_POSE_USE_POSE1Goes to the pre-docking pose before docking.
    PREP_POSE_SKIP_POSE2Docks before going to the pre-docking pose.id_startuint32Starting ID
    PREP_POSE_ONLY_POSE3Goes to the pre-docking pose, and then returns SUCCESS without docking.id_enduint32Ending ID
    PREP_POSE_UNDOCK4Use this enum to undock a currently docked robot.typeDockTypeType of dock for this range

    -
    +

    -
    -

    docking/docking_service.proto

    -

    -
    -

    DockingService

    -

    The DockingService provides an interface to dock and undock the robot from Spot Docks, -as well as get feedback on command status, and get the current docked status of the robot.

    +
    +

    DockState

    +

    Message describing the overall dock state of the robot, including power & comms connections.
    Not tied to any particular DockingCommand ID.
    Note: [*] indicates fields which are only valid if the status is DOCK_STATUS_DOCKED or DOCK_STATUS_DOCKING
    or DOCK_STATUS_UNDOCKING.
    Note: [^] indicates fields which are only valid if the status is DOCK_STATUS_DOCKED. \

    - - - + + - - - - + + + - - - - + + + - - - - + + + - - - - + + + -
    Method NameRequest TypeResponse TypeFieldType Description
    DockingCommandDockingCommandRequestDockingCommandResponseStarts a docking command on the robot.statusDockState.DockedStatusStatus of if the robot is on dock
    DockingCommandFeedbackDockingCommandFeedbackRequestDockingCommandFeedbackResponseCheck the status of a docking command.dock_typeDockType[*] Type of the dock
    GetDockingConfigGetDockingConfigRequestGetDockingConfigResponseGet the configured dock ID ranges.dock_iduint32[*] ID of the dock
    GetDockingStateGetDockingStateRequestGetDockingStateResponseGet the robot's docking statepower_statusDockState.LinkStatus[^] Status of power detection from the dock

    -
    +

    -
    -

    estop.proto

    -

    -
    -

    DeregisterEstopEndpointRequest

    -

    Deregister the specified E-Stop endpoint registration.

    +
    +

    DockingCommandFeedbackRequest

    +

    Message to get the status of a previously issued DockingCommand

    @@ -7953,25 +7995,20 @@

    DeregisterEstopEndpointRequestRequestHeader -

    - - - - - + + - - - + + + -
    Common request header
    target_endpointEstopEndpointThe endpoint to deregister.bosdyn.api.RequestHeaderCommon request header.
    target_config_idstringID of the configuration we are registering against.docking_command_iduint32Unique identifier of the command to get feedback for.

    +

    -
    -

    DeregisterEstopEndpointResponse

    -

    Response to E-Stop endpoint deregistration request.

    +
    +

    DockingCommandFeedbackResponse

    +

    Response to a DockingCommandFeedbackRequest for a particualar docking command ID

    @@ -7983,26 +8020,26 @@

    DeregisterEstopEndpointResponseResponseHeader -

    + + - - - + + + - - + + -
    Common resonse header.bosdyn.api.ResponseHeaderCommon response header.
    requestDeregisterEstopEndpointRequestCopy of the initial request.lease_use_resultbosdyn.api.LeaseUseResultDetails about how the lease was used (unset if unknown).
    statusDeregisterEstopEndpointResponse.StatusStatus code for the response.DockingCommandFeedbackResponse.StatusCurrent feedback of specified command ID.

    +

    -
    -

    EstopCheckInRequest

    -

    Client request for setting/maintaining an E-Stop system level. -After the first CheckIn, must include response to previous challenge.

    +
    +

    DockingCommandRequest

    +

    Message to command the robot to dock.
    Note: If the robot is docked, you can undock the robot by issuing a command with +prep_pose_behavior=PREP_POSE_UNDOCK. If undocking, docking_station_id is not required.

    @@ -8014,35 +8051,40 @@

    EstopCheckInRequestRequestHeader +

    - - - + + + - - - + + + - - - + + + - - - + + + + + + + + -
    bosdyn.api.RequestHeader Common request header.
    endpointEstopEndpointThe endpoint making the request.leasebosdyn.api.LeaseThe Lease to show ownership of the robot.
    challengeuint64Challenge being responded to. Don't set if this is the first EstopCheckInRequest.docking_station_iduint32ID of docking station to dock at. This is ignored if undocking the robot, the current dock is used.
    responseuint64Response to above challenge. Don't set if this is the first EstopCheckInRequest.clock_identifierstringIdentifier provided by the time sync service to verify time sync between robot and client.
    stop_levelEstopStopLevelAssert this stop level.end_timegoogle.protobuf.TimestampThe timestamp (in robot time) by which a command must finish executing. This is a required field and used to prevent runaway commands.
    prep_pose_behaviorPrepPoseBehavior[Optional] Specify the prep pose behavior

    +

    -
    -

    EstopCheckInResponse

    -

    Server response to EstopCheckInRequest.

    +
    +

    DockingCommandResponse

    +

    Response to a DockingCommandRequest

    @@ -8054,30 +8096,29 @@

    EstopCheckInResponse

    - + - - - + + + - - - + + + - - - + + + -
    headerResponseHeaderbosdyn.api.ResponseHeader Common response header.
    requestEstopCheckInRequestCopy of initial request.lease_use_resultbosdyn.api.LeaseUseResultDetails about how the lease was used.
    challengeuint64Next challenge to answer.statusDockingCommandResponse.StatusResult of issued command.
    statusEstopCheckInResponse.StatusStatus code for the response.docking_command_iduint32Unique identifier for the command (if accepted, status=STATUS_OK).

    +

    -
    -

    EstopConfig

    -

    Configuration of a root / server.

    +
    +

    GetDockingConfigRequest

    @@ -8088,21 +8129,15 @@

    EstopConfigEstopEndpoint -

    - - - - - + + + -
    EstopEndpoints that are part of this configuration. Unique IDs do not have to be filled out, but can be.
    unique_idstringUnique ID for this configuration.headerbosdyn.api.RequestHeaderCommon request header.

    +

    -
    -

    EstopEndpoint

    -

    An to the robot software-E-Stop system.

    +
    +

    GetDockingConfigResponse

    @@ -8113,36 +8148,41 @@

    EstopEndpointstring -

    - - - - - + + + - - - + + + + +
    Role of this endpoint. Should be a user-friendly string, e.g. "OCU".
    namestringName of this endpoint. Specifies a thing to fill the given role, e.g. "patrol-ocu01"headerbosdyn.api.ResponseHeaderCommon response header.
    unique_idstringUnique ID assigned by the server.dock_configsConfigRangeA series of ConfigRange specifying details for dock ID numbers.

    +
    +
    +

    GetDockingStateRequest

    +

    Message to get the overall docking state

    + + - - - + + + + + - - - + + + -
    timeoutgoogle.protobuf.DurationMaximum delay between challenge and response for this endpoint prior to soft power off handling. After timeout seconds has passed, the robot will try to get to a safe state prior to disabling motor power. The robot response is equivalent to an ESTOP_LEVEL_SETTLE_THEN_CUT which may involve the robot sitting down in order to prepare for disabling motor power.FieldTypeDescription
    cut_power_timeoutgoogle.protobuf.DurationOptional maximum delay between challenge and response for this endpoint prior to disabling motor power. After cut_power_timeout seconds has passed, motor power will be disconnected immediately regardless of current robot state. If this value is not set robot will default to timeout plus a nominal expected duration to reach a safe state. In practice this is typically 3-4 seconds. The response is equivalent to an ESTOP_LEVEL_CUT.headerbosdyn.api.RequestHeaderCommon request header.

    +

    -
    -

    EstopEndpointWithStatus

    -

    EstopEndpoint with some extra status data.

    +
    +

    GetDockingStateResponse

    +

    Response of a GetDockingStateRequest

    @@ -8153,227 +8193,346 @@

    EstopEndpointWithStatus

    - - - - - - - - + + + - - - + + + -
    endpointEstopEndpointThe endpoint.
    stop_levelEstopStopLevelStop level most recently requested by the endpoint.headerbosdyn.api.ResponseHeaderCommon response header.
    time_since_valid_responsegoogle.protobuf.DurationTime since a valid response was provided by the endpoint.dock_stateDockState

    +

    -
    -

    EstopSystemStatus

    -

    Status of Estop system.

    +
    +

    DockState.DockedStatus

    - - + + - - - + + + - - - + + + - - - + + + + + + + + + + + + + -
    FieldTypeNameNumber Description
    endpointsEstopEndpointWithStatusStatus for all available endpoints.DOCK_STATUS_UNKNOWN0Unknown
    stop_levelEstopStopLevelCurrent stop level for the system. Will be the most-restrictive stop level specified by an endpoint, or a stop level asserted by the system as a whole (e.g. if an endpoint timed out).DOCK_STATUS_DOCKED1Robot is detected as on a dock
    stop_level_detailsstringHuman-readable information on the stop level.DOCK_STATUS_DOCKING2Robot is currently running a docking command
    DOCK_STATUS_UNDOCKED3Robot is not detected as on dock
    DOCK_STATUS_UNDOCKING4Robot is currently running an undocking command

    +

    -
    -

    GetEstopConfigRequest

    -

    Get the active EstopConfig.

    +
    +

    DockState.LinkStatus

    - - + + - - - + + + - - - + + + + + + + + + + + + + -
    FieldTypeNameNumber Description
    headerRequestHeaderCommon request header.LINK_STATUS_UNKNOWN0Unknown or Not applicable
    target_config_idstringThe 'unique_id' of EstopConfig to get.LINK_STATUS_DETECTING3The link status is being detected
    LINK_STATUS_CONNECTED1The link is detected as connected
    LINK_STATUS_ERROR2The link could not be detected

    +

    -
    -

    GetEstopConfigResponse

    -

    Response to EstopConfigRequest.

    +
    +

    DockType

    +

    Type of dock

    - - + + - - - + + + - - - + + + - - - + + + -
    FieldTypeNameNumber Description
    headerResponseHeaderCommon response header.DOCK_TYPE_UNKNOWN0Unknown type of dock
    requestGetEstopConfigRequestCopy of the request.DOCK_TYPE_CONTACT_PROTOTYPE2Prototype version SpotDock
    active_configEstopConfigThe currently active configuration.DOCK_TYPE_SPOT_DOCK3Production version SpotDock

    +

    -
    -

    GetEstopSystemStatusRequest

    -

    Ask for the current status of the Estop system.

    +
    +

    DockingCommandFeedbackResponse.Status

    - - + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -
    FieldTypeNameNumber Description
    headerRequestHeaderCommon request header.STATUS_UNKNOWN0Status is not specified.
    STATUS_IN_PROGRESS1Docking command is executing.
    STATUS_DOCKED2Docking command succeeded, the robot is docked.
    STATUS_AT_PREP_POSE11Final success state for PREP_POSE_ONLY_POSE or PREP_POSE_UNDOCK.
    STATUS_MISALIGNED10Misaligned was detected between the robot and the dock. The docking command was aborted to save an ending up in an unrecoverable state, please try again.
    STATUS_OLD_DOCKING_COMMAND3This DockingCommand overridden by new docking command.
    STATUS_ERROR_DOCK_LOST4ERROR: The sensed dock has been lost and is no longer found.
    STATUS_ERROR_LEASE5ERROR: Lease rejected.
    STATUS_ERROR_COMMAND_TIMED_OUT6ERROR: End time has been reached.
    STATUS_ERROR_NO_TIMESYNC7ERROR: No Timesync with system.
    STATUS_ERROR_TOO_DISTANT8ERROR: Provided end time too far in the future.
    STATUS_ERROR_NOT_AVAILABLE12ERROR: The dock is not available for docking.
    STATUS_ERROR_SYSTEM9ERROR: Internal system error during execution This error cannot be resolved by issuing a new DockingCommand Check the returned message for details

    +

    -
    -

    GetEstopSystemStatusResponse

    -

    Respond with the current Estop system status.

    +
    +

    DockingCommandResponse.Status

    - - + + - - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -
    FieldTypeNameNumber Description
    headerResponseHeaderCommon response header.STATUS_UNKNOWN0Status is not specified.
    statusEstopSystemStatusStatus of the Estop system.STATUS_OK1Docking command accepted
    STATUS_ERROR_LEASE4ERROR: Lease rejected
    STATUS_ERROR_DOCK_NOT_FOUND5ERROR: Dock fiducial not found.
    STATUS_ERROR_NOT_DOCKED6ERROR: Trying to undock while not docked
    STATUS_ERROR_GRIPPER_HOLDING_ITEM8ERROR: Trying to dock when the arm is holding an object.
    STATUS_ERROR_NOT_AVAILABLE9ERROR: The dock is not available for docking.
    STATUS_ERROR_SYSTEM7ERROR: Internal system error during execution This error cannot be resolved by issuing a new DockingCommand Check the returned message for details

    +

    -
    -

    RegisterEstopEndpointRequest

    -

    Register an endpoint. -EstopEndpoints must be registered before they can send commands or request challenges.

    +
    +

    PrepPoseBehavior

    +

    Defines how and whether we use the “pre-docking” pose.

    - - + + - - - + + + - - - + + + - - - + + + - - - + + + + + + + + -
    FieldTypeNameNumber Description
    headerRequestHeaderCommon request headerPREP_POSE_UNKNOWN0Default behavior, equivalent to PREP_POSE_USE_POSE.
    target_endpointEstopEndpointThe endpoint to replace. Set the endpoint's unique ID if replacing an active endpoint.PREP_POSE_USE_POSE1Goes to the pre-docking pose before docking.
    target_config_idstringID of the configuration we are registering against.PREP_POSE_SKIP_POSE2Docks before going to the pre-docking pose.
    new_endpointEstopEndpointThe description of the new endpoint. Do not set the unique ID. It will be ignored.PREP_POSE_ONLY_POSE3Goes to the pre-docking pose, and then returns SUCCESS without docking.
    PREP_POSE_UNDOCK4Use this enum to undock a currently docked robot.

    +

    -
    -

    RegisterEstopEndpointResponse

    -

    Response to registration request.

    +
    +
    +

    docking/docking_service.proto

    +

    +
    +

    DockingService

    +

    The DockingService provides an interface to dock and undock the robot from Spot Docks, +as well as get feedback on command status, and get the current docked status of the robot.

    - - + + + - - - + + + + - - - + + + + - - - + + + + - - - + + + + -
    FieldTypeMethod NameRequest TypeResponse Type Description
    headerResponseHeaderCommon response headerDockingCommandDockingCommandRequestDockingCommandResponseStarts a docking command on the robot.
    requestRegisterEstopEndpointRequestCopy of the initial request.DockingCommandFeedbackDockingCommandFeedbackRequestDockingCommandFeedbackResponseCheck the status of a docking command.
    new_endpointEstopEndpointThe resulting endpoint on success.GetDockingConfigGetDockingConfigRequestGetDockingConfigResponseGet the configured dock ID ranges.
    statusRegisterEstopEndpointResponse.StatusStatus code for the response.GetDockingStateGetDockingStateRequestGetDockingStateResponseGet the robot's docking state

    +

    -
    -

    SetEstopConfigRequest

    -

    Set a new active EstopConfig.

    +
    +
    +

    estop.proto

    +

    +
    +

    DeregisterEstopEndpointRequest

    +

    Deregister the specified E-Stop endpoint registration.

    @@ -8386,24 +8545,24 @@

    SetEstopConfigRequest

    - + - - - + + + - + -
    header RequestHeaderCommon request header.Common request header
    configEstopConfigNew configuration to set.target_endpointEstopEndpointThe endpoint to deregister.
    target_config_id stringThe 'unique_id' of EstopConfig to replace, if replacing one.ID of the configuration we are registering against.

    +

    -
    -

    SetEstopConfigResponse

    -

    Response to EstopConfigRequest.

    +
    +

    DeregisterEstopEndpointResponse

    +

    Response to E-Stop endpoint deregistration request.

    @@ -8416,310 +8575,250 @@

    SetEstopConfigResponse

    - + - - - - - - - + + - - + + -
    header ResponseHeaderCommon response header.Common resonse header.
    requestSetEstopConfigRequestCopy of the request.
    active_configEstopConfigThe currently active configuration.DeregisterEstopEndpointRequestCopy of the initial request.
    statusSetEstopConfigResponse.StatusDeregisterEstopEndpointResponse.StatusStatus code for the response.

    +

    -
    -

    DeregisterEstopEndpointResponse.Status

    +
    +

    EstopCheckInRequest

    +

    Client request for setting/maintaining an E-Stop system level. +After the first CheckIn, must include response to previous challenge.

    - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + + + + + + -
    NameNumberDescriptionFieldTypeDescription
    STATUS_UNKNOWN0An unknown / unexpected error occurred.headerRequestHeaderCommon request header.
    STATUS_SUCCESS1Request succeeded.endpointEstopEndpointThe endpoint making the request.
    STATUS_ENDPOINT_MISMATCH2Target endpoint did not match.challengeuint64Challenge being responded to. Don't set if this is the first EstopCheckInRequest.
    STATUS_CONFIG_MISMATCH3Registered to wrong configuration.responseuint64Response to above challenge. Don't set if this is the first EstopCheckInRequest.
    stop_levelEstopStopLevelAssert this stop level.

    +

    -
    -

    EstopCheckInResponse.Status

    +
    +

    EstopCheckInResponse

    +

    Server response to EstopCheckInRequest.

    - - + + - - - + + + - - - + + + - - - + + + - - - + + + -
    NameNumberFieldType Description
    STATUS_UNKNOWN0Unknown error occurred.headerResponseHeaderCommon response header.
    STATUS_OK1Valid challenge has been returned.requestEstopCheckInRequestCopy of initial request.
    STATUS_ENDPOINT_UNKNOWN2The endpoint specified in the request is not registered.challengeuint64Next challenge to answer.
    STATUS_INCORRECT_CHALLENGE_RESPONSE5The challenge and/or response was incorrect.statusEstopCheckInResponse.StatusStatus code for the response.

    +

    -
    -

    EstopStopLevel

    -

    The state of the E-Stop system.

    +
    +

    EstopConfig

    +

    Configuration of a root / server.

    - - + + - - - - - - - - - - - - - + + + - - - + + + -
    NameNumberFieldType Description
    ESTOP_LEVEL_UNKNOWN0Invalid stop level.
    ESTOP_LEVEL_CUT1Immediately cut power to the actuators.
    ESTOP_LEVEL_SETTLE_THEN_CUT2Prepare for loss of actuator power, then cut power.endpointsEstopEndpointEstopEndpoints that are part of this configuration. Unique IDs do not have to be filled out, but can be.
    ESTOP_LEVEL_NONE4No-stop level. The endpoint believes the robot is safe to operate.unique_idstringUnique ID for this configuration.

    +

    -
    -

    RegisterEstopEndpointResponse.Status

    +
    +

    EstopEndpoint

    +

    An to the robot software-E-Stop system.

    - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + -
    NameNumberFieldType Description
    STATUS_UNKNOWN0An unknown / unexpected error occurred.rolestringRole of this endpoint. Should be a user-friendly string, e.g. "OCU".
    STATUS_SUCCESS1Request succeeded.namestringName of this endpoint. Specifies a thing to fill the given role, e.g. "patrol-ocu01"
    STATUS_ENDPOINT_MISMATCH2Target endpoint did not match.unique_idstringUnique ID assigned by the server.
    STATUS_CONFIG_MISMATCH3Registered to wrong configuration.timeoutgoogle.protobuf.DurationMaximum delay between challenge and response for this endpoint prior to soft power off handling. After timeout seconds has passed, the robot will try to get to a safe state prior to disabling motor power. The robot response is equivalent to an ESTOP_LEVEL_SETTLE_THEN_CUT which may involve the robot sitting down in order to prepare for disabling motor power.
    STATUS_INVALID_ENDPOINT4New endpoint was invalid.cut_power_timeoutgoogle.protobuf.DurationOptional maximum delay between challenge and response for this endpoint prior to disabling motor power. After cut_power_timeout seconds has passed, motor power will be disconnected immediately regardless of current robot state. If this value is not set robot will default to timeout plus a nominal expected duration to reach a safe state. In practice this is typically 3-4 seconds. The response is equivalent to an ESTOP_LEVEL_CUT.

    +

    -
    -

    SetEstopConfigResponse.Status

    +
    +

    EstopEndpointWithStatus

    +

    EstopEndpoint with some extra status data.

    - - + + - - - - - - - - + + + - - - + + + - - - + + + -
    NameNumberFieldType Description
    STATUS_UNKNOWN0An unknown / unexpected error occurred.
    STATUS_SUCCESS1Request succeeded.endpointEstopEndpointThe endpoint.
    STATUS_INVALID_ID2Tried to replace a EstopConfig, but provided bad ID.stop_levelEstopStopLevelStop level most recently requested by the endpoint.
    STATUS_MOTORS_ON4You cannot set a configuration while the motors are on.time_since_valid_responsegoogle.protobuf.DurationTime since a valid response was provided by the endpoint.

    -
    +

    -
    -

    estop_service.proto

    -

    -
    -

    EstopService

    -

    The software robot E-Stop system:

    -
      -
    1. Uses challenge-style communication to enforce end user (aka “originators”) connection -for Authority to Operate (ATO).

    2. -
    3. Offers the ability to issue a direct denial of ATO. -The EstopService provides a service interface for the robot EStop/Authority to operate the system.

    4. -
    +
    +

    EstopSystemStatus

    +

    Status of Estop system.

    - - - + + - - - - - - - - - - - - - - - - - - - - - - + + + - - - - + + + - - - - + + + -
    Method NameRequest TypeResponse TypeFieldType Description
    RegisterEstopEndpointRegisterEstopEndpointRequestRegisterEstopEndpointResponseRegister an Estop "originator" or "endpoint". This may be a replacement for another active endpoint.
    DeregisterEstopEndpointDeregisterEstopEndpointRequestDeregisterEstopEndpointResponseDeregister the requested estop endpoint.
    EstopCheckInEstopCheckInRequestEstopCheckInResponseAnswer challenge from previous response (unless this is the first call), and request a stop level.
    GetEstopConfigGetEstopConfigRequestGetEstopConfigResponseRequest the current EstopConfig, describing the expected set of endpoints.endpointsEstopEndpointWithStatusStatus for all available endpoints.
    SetEstopConfigSetEstopConfigRequestSetEstopConfigResponseSet a new active EstopConfig.stop_levelEstopStopLevelCurrent stop level for the system. Will be the most-restrictive stop level specified by an endpoint, or a stop level asserted by the system as a whole (e.g. if an endpoint timed out).
    GetEstopSystemStatusGetEstopSystemStatusRequestGetEstopSystemStatusResponseAsk for the current status of the estop system.stop_level_detailsstringHuman-readable information on the stop level.

    -
    +

    -
    -

    fault_service.proto

    -

    -
    -

    FaultService

    -

    The service fault service enables modification of the robot state ServiceFaultState.

    +
    +

    GetEstopConfigRequest

    +

    Get the active EstopConfig.

    - - - + + - - - - + + + - - - - + + + -
    Method NameRequest TypeResponse TypeFieldType Description
    TriggerServiceFaultTriggerServiceFaultRequestTriggerServiceFaultResponseSends a ServiceFault to be reporting in robot state.headerRequestHeaderCommon request header.
    ClearServiceFaultClearServiceFaultRequestClearServiceFaultResponseClears an active ServiceFault from robot state.target_config_idstringThe 'unique_id' of EstopConfig to get.

    -
    -
    -
    -

    full_body_command.proto

    -

    -
    -

    FullBodyCommand

    -

    The robot command message to specify a basic command that requires full control of the entire -robot to be completed.

    -

    +

    -
    -

    FullBodyCommand.Feedback

    -

    The feedback for the fully body command that will provide information on the progress -of the robot command.

    +
    +

    GetEstopConfigResponse

    +

    Response to EstopConfigRequest.

    @@ -8730,46 +8829,26 @@

    FullBodyCommand.FeedbackStopCommand.Feedback -

    - - - - - - - - - - - - - - - - - - - - + + + - - - + + + - - - + + + -
    Feedback for the stop command request.
    freeze_feedbackFreezeCommand.FeedbackFeedback for the freeze command request.
    selfright_feedbackSelfRightCommand.FeedbackFeedback for the self-right command request.
    safe_power_off_feedbackSafePowerOffCommand.FeedbackFeedback for the safe power off command request.
    battery_change_pose_feedbackBatteryChangePoseCommand.FeedbackFeedback for the battery change pose command request.headerResponseHeaderCommon response header.
    payload_estimation_feedbackPayloadEstimationCommand.FeedbackFeedback for the payload estimation command request.requestGetEstopConfigRequestCopy of the request.
    statusRobotCommandFeedbackStatus.Statusactive_configEstopConfigThe currently active configuration.

    +

    -
    -

    FullBodyCommand.Request

    -

    The full body request must be one of the basic command primitives.

    +
    +

    GetEstopSystemStatusRequest

    +

    Ask for the current status of the Estop system.

    @@ -8780,50 +8859,16 @@

    FullBodyCommand.Request

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + -
    stop_requestStopCommand.RequestCommand to stop the robot.
    freeze_requestFreezeCommand.RequestCommand to freeze all joints of the robot.
    selfright_requestSelfRightCommand.RequestCommand to self-right the robot to a ready position.
    safe_power_off_requestSafePowerOffCommand.RequestCommand to safely power off the robot.
    battery_change_pose_requestBatteryChangePoseCommand.RequestCommand to put the robot in a position to easily change the battery.
    payload_estimation_requestPayloadEstimationCommand.RequestCommand to perform payload mass property estimation
    paramsgoogle.protobuf.AnyRobot specific command parameters.headerRequestHeaderCommon request header.

    -
    +

    -
    -

    geometry.proto

    -

    -
    -

    Area

    -

    Represents an area in the XY plane.

    +
    +

    GetEstopSystemStatusResponse

    +

    Respond with the current Estop system status.

    @@ -8834,23 +8879,22 @@

    Area

    - - - + + + - - - + + + -
    polygonPolygonheaderResponseHeaderCommon response header.
    circleCirclestatusEstopSystemStatusStatus of the Estop system.

    +

    -
    -

    Bounds

    -

    Represents bounds on a value, such that lower < value < upper. -If you do not want to specify one side of the bound, set it to -an appropriately large (or small) number.

    +
    +

    RegisterEstopEndpointRequest

    +

    Register an endpoint. +EstopEndpoints must be registered before they can send commands or request challenges.

    @@ -8861,41 +8905,31 @@

    Boundsdouble -

    + + + - - - + + + - -
    headerRequestHeaderCommon request header
    upperdoubletarget_endpointEstopEndpointThe endpoint to replace. Set the endpoint's unique ID if replacing an active endpoint.

    -
    -
    -

    Box2

    -

    Geometric primitive describing a two-dimensional box.

    - - - - - + + + - - - - - + + + -
    FieldTypeDescriptiontarget_config_idstringID of the configuration we are registering against.
    sizeVec2new_endpointEstopEndpointThe description of the new endpoint. Do not set the unique ID. It will be ignored.

    +

    -
    -

    Box2WithFrame

    -

    Geometric primitive to describe a 2D box in a specific frame.

    +
    +

    RegisterEstopEndpointResponse

    +

    Response to registration request.

    @@ -8906,26 +8940,31 @@

    Box2WithFrameBox2 -

    + + + - - - + + + - - - + + + + + + + + -
    The box is specified with width (y) and length (x), and the full box is fixed at an origin, where it's sides are along the coordinate frame's axes.headerResponseHeaderCommon response header
    frame_namestringThe pose of the axis-aligned box is in 'frame_name'.requestRegisterEstopEndpointRequestCopy of the initial request.
    frame_name_tform_boxSE3PoseThe transformation of the axis-aligned box into the desired frame (specified above).new_endpointEstopEndpointThe resulting endpoint on success.
    statusRegisterEstopEndpointResponse.StatusStatus code for the response.

    +

    -
    -

    Box3

    -

    Geometric primitive describing a three-dimensional box.

    +
    +

    SetEstopConfigRequest

    +

    Set a new active EstopConfig.

    @@ -8936,16 +8975,26 @@

    Box3

    - - - + + + + + + + + + + + + + -
    sizeVec3headerRequestHeaderCommon request header.
    configEstopConfigNew configuration to set.
    target_config_idstringThe 'unique_id' of EstopConfig to replace, if replacing one.

    +

    -
    -

    Box3WithFrame

    -

    Geometric primitive to describe a 3D box in a specific frame.

    +
    +

    SetEstopConfigResponse

    +

    Response to EstopConfigRequest.

    @@ -8956,378 +9005,317 @@

    Box3WithFrameBox3 -

    + + + - - - - - - - - + + + - -
    The box width (y), length (x), and height (z) are interpreted in, and the full box is fixed at an origin, where it's sides are along the coordinate frame's axes.headerResponseHeaderCommon response header.
    frame_namestringThe pose of the axis-aligned box is in 'frame_name'.
    frame_name_tform_boxSE3PoseThe transformation of the axis-aligned box into the desired frame (specified above).requestSetEstopConfigRequestCopy of the request.

    -
    -
    -

    Circle

    -

    Represents a circular 2D area.

    - - - - - + + + - - - - + + - - - - - -
    FieldTypeDescriptionactive_configEstopConfigThe currently active configuration.
    center_ptVec2statusSetEstopConfigResponse.Status
    radiusdoubleDimensions in m from center_pt.

    +

    -
    -

    CylindricalCoordinate

    -

    Cylindrical coordinates are a generalization of polar coordiates, adding a -height -axis. See (http://mathworld.wolfram.com/CylindricalCoordinates.html) for -more details.

    +
    +

    DeregisterEstopEndpointResponse.Status

    - - + + - - - + + + - - - + + + - - - + + + - -
    FieldTypeNameNumber Description
    rdoubleRadial coordinateSTATUS_UNKNOWN0An unknown / unexpected error occurred.
    thetadoubleAzimuthal coordinateSTATUS_SUCCESS1Request succeeded.
    zdoubleVertical coordianteSTATUS_ENDPOINT_MISMATCH2Target endpoint did not match.

    -
    -
    -

    FrameTreeSnapshot

    -

    A frame is a named location in space.
    For example, the following frames are defined by the API: \

    -
      -
    • “body”: A frame centered on the robot’s body. \

    • -
    • “vision”: A non-moving (inertial) frame that is the robot’s best -estimate of a fixed location in the world. It is based on -both dead reckoning and visual analysis of the world. \

    • -
    • “odom”: A non-moving (inertial) frame that is based on the kinematic -odometry of the robot only.
      Additional frames are available for robot joints, sensors, and items -detected in the world. \

    • -
    -

    The FrameTreeSnapshot represents the relationships between the frames that the robot -knows about at a particular point in time. For example, with the FrameTreeSnapshot, -an API client can determine where the “body” is relative to the “vision”. \

    -

    To reduce data bandwidth, the FrameTreeSnapshot will typically contain -a small subset of all known frames. By default, all services MUST -include “vision”, “body”, and “odom” frames in the FrameTreeSnapshot, but -additional frames can also be included. For example, an Image service -would likely include the frame located at the base of the camera lens -where the picture was taken. \

    -

    Frame relationships are expressed as edges between “parent” frames and -“child” frames, with an SE3Pose indicating the pose of the “child” frame -expressed in the “child” frame. These edges are included in the edge_map -field. For example, if frame “hand” is 1m in front of the frame “shoulder”, -then the FrameTreeSnapshot might contain:
    edge_map {
    key: “hand”
    value: {
    parent_frame_name: “shoulder”
    parent_tform_child: {
    position: {
    x: 1.0
    y: 0.0
    z: 0.0
    }
    }
    }
    } \

    -

    Frame relationships can be inverted. So, to find where the “shoulder” -is in relationship the “hand”, the parent_tform_child pose in the edge -above can be inverted:
    hand_tform_shoulder = shoulder_tform_hand.inverse()
    Frame relationships can also be concatenated. If there is an additional -edge specifying the pose of the “shoulder” relative to the “body”, then -to find where the “hand” is relative to the “body” do:
    body_tform_hand = body_tform_shoulder * shoulder_tform_hand \

    -

    The two properties above reduce data size. Instead of having to send N^2 -edge_map entries to represent all relationships between N frames, -only N edge_map entries need to be sent. Clients will need to determine -the chain of edges to follow to get from one frame to another frame, -and then do inversion and concatentation to generate the appropriate pose. \

    -

    Note that all FrameTreeSnapshots are expected to be a single rooted tree. -The syntax for FrameTreeSnapshot could also support graphs with -cycles, or forests of trees - but clients should treat those as invalid -representations. \

    - - - - - + + + - - - - - + + + -
    FieldTypeDescriptionSTATUS_CONFIG_MISMATCH3Registered to wrong configuration.
    child_to_parent_edge_mapFrameTreeSnapshot.ChildToParentEdgeMapEntrychild_to_parent_edge_map maps the child frame name to the ParentEdge. In aggregate, this forms the tree structure.STATUS_MOTORS_ON4You cannot deregister an endpoint while the motors are on.

    +

    -
    -

    FrameTreeSnapshot.ChildToParentEdgeMapEntry

    +
    +

    EstopCheckInResponse.Status

    - - + + - - - - - - - - + + + - -
    FieldTypeNameNumber Description
    keystring
    valueFrameTreeSnapshot.ParentEdgeSTATUS_UNKNOWN0Unknown error occurred.

    -
    -
    -

    FrameTreeSnapshot.ParentEdge

    -

    ParentEdge represents the relationship from a child frame to a parent frame.

    - - - - - + + + - - - - - + + + - - - + + + -
    FieldTypeDescriptionSTATUS_OK1Valid challenge has been returned.
    parent_frame_namestringThe name of the parent frame. Must be non-empty. If parent_frame_name is not a key in edge_map, it is the root of the tree.STATUS_ENDPOINT_UNKNOWN2The endpoint specified in the request is not registered.
    parent_tform_childSE3PoseTransform representing the pose of the child frame in the parent's frame.STATUS_INCORRECT_CHALLENGE_RESPONSE5The challenge and/or response was incorrect.

    +

    -
    -

    Matrix

    -

    Represents a row-major order matrix of doubles.

    +
    +

    EstopStopLevel

    +

    The state of the E-Stop system.

    - - + + - - - + + + - - - + + + - - - + + + + + + + + -
    FieldTypeNameNumber Description
    rowsint32ESTOP_LEVEL_UNKNOWN0Invalid stop level.
    colsint32ESTOP_LEVEL_CUT1Immediately cut power to the actuators.
    valuesdoubleESTOP_LEVEL_SETTLE_THEN_CUT2Prepare for loss of actuator power, then cut power.
    ESTOP_LEVEL_NONE4No-stop level. The endpoint believes the robot is safe to operate.

    +

    -
    -

    Plane

    -

    Plane primitive, described with a point and normal.

    +
    +

    RegisterEstopEndpointResponse.Status

    - - + + - - - + + + - - - + + + + + + + + + + + + + + + + + + -
    FieldTypeNameNumber Description
    pointVec3A point on the plane.STATUS_UNKNOWN0An unknown / unexpected error occurred.
    normalVec3The direction of the planes normal.STATUS_SUCCESS1Request succeeded.
    STATUS_ENDPOINT_MISMATCH2Target endpoint did not match.
    STATUS_CONFIG_MISMATCH3Registered to wrong configuration.
    STATUS_INVALID_ENDPOINT4New endpoint was invalid.

    +

    -
    -

    PolyLine

    -

    Multi-part, 1D line segments defined by a series of points.

    +
    +

    SetEstopConfigResponse.Status

    - - + + - - - + + + - -
    FieldTypeNameNumber Description
    pointsVec2STATUS_UNKNOWN0An unknown / unexpected error occurred.

    -
    -
    -

    Polygon

    -

    Polygon in the XY plane. -May be concave, but should not self-intersect. Vertices can be specified in either -clockwise or counterclockwise orders.

    - - - - - + + + - - - - - + + + + + + + + -
    FieldTypeDescriptionSTATUS_SUCCESS1Request succeeded.
    vertexesVec2STATUS_INVALID_ID2Tried to replace a EstopConfig, but provided bad ID.
    STATUS_MOTORS_ON4You cannot set a configuration while the motors are on.

    +

    -
    -

    PolygonWithExclusions

    -

    Represents a region in the XY plane that consists of a single polygon -from which polygons representing exclusion areas may be subtracted.

    -

    A point is considered to be inside the region if it is inside the inclusion -polygon and not inside any of the exclusion polygons.

    -

    Note that while this can be used to represent a polygon with holes, that -exclusions are not necessarily holes: An exclusion polygon may not be -completely inside the inclusion polygon.

    +
    +
    +

    estop_service.proto

    +

    +
    +

    EstopService

    +

    The software robot E-Stop system:

    +
      +
    1. Uses challenge-style communication to enforce end user (aka “originators”) connection +for Authority to Operate (ATO).

    2. +
    3. Offers the ability to issue a direct denial of ATO. +The EstopService provides a service interface for the robot EStop/Authority to operate the system.

    4. +
    - - + + + - - - - - - - - + + + + - -
    FieldTypeMethod NameRequest TypeResponse Type Description
    inclusionPolygon
    exclusionsPolygonRegisterEstopEndpointRegisterEstopEndpointRequestRegisterEstopEndpointResponseRegister an Estop "originator" or "endpoint". This may be a replacement for another active endpoint.

    -
    -
    -

    Quaternion

    -

    Quaternion primitive. A quaternion can be used to describe the rotation.

    - - - - - + + + + - - - - - + + + + - - - + + + + - - - + + + + - - - + + + + -
    FieldTypeDescriptionDeregisterEstopEndpointDeregisterEstopEndpointRequestDeregisterEstopEndpointResponseDeregister the requested estop endpoint.
    xdoubleEstopCheckInEstopCheckInRequestEstopCheckInResponseAnswer challenge from previous response (unless this is the first call), and request a stop level.
    ydoubleGetEstopConfigGetEstopConfigRequestGetEstopConfigResponseRequest the current EstopConfig, describing the expected set of endpoints.
    zdoubleSetEstopConfigSetEstopConfigRequestSetEstopConfigResponseSet a new active EstopConfig.
    wdoubleGetEstopSystemStatusGetEstopSystemStatusRequestGetEstopSystemStatusResponseAsk for the current status of the estop system.

    +

    -
    -

    SE2Pose

    -

    Geometric primitive to describe 2D position and rotation.

    +
    +
    +

    fault_service.proto

    +

    +
    +

    FaultService

    +

    The service fault service enables modification of the robot state ServiceFaultState.

    - - + + + - - - + + + + - - - + + + + -
    FieldTypeMethod NameRequest TypeResponse Type Description
    positionVec2(m)TriggerServiceFaultTriggerServiceFaultRequestTriggerServiceFaultResponseSends a ServiceFault to be reporting in robot state.
    angledouble(rad)ClearServiceFaultClearServiceFaultRequestClearServiceFaultResponseClears an active ServiceFault from robot state.

    +

    -
    -

    SE2Velocity

    -

    Geometric primitive that describes a 2D velocity through it’s linear and angular components.

    +
    +
    +

    full_body_command.proto

    +

    +
    +

    FullBodyCommand

    +

    The robot command message to specify a basic command that requires full control of the entire +robot to be completed.

    +

    +
    +
    +

    FullBodyCommand.Feedback

    +

    The feedback for the fully body command that will provide information on the progress +of the robot command.

    @@ -9338,48 +9326,51 @@

    SE2VelocityVec2 -

    + + + - - - + + + - -
    (m/s)stop_feedbackStopCommand.FeedbackFeedback for the stop command request.
    angulardouble(rad/s)freeze_feedbackFreezeCommand.FeedbackFeedback for the freeze command request.

    -
    -
    -

    SE2VelocityLimit

    -

    Geometric primitive to couple minimum and maximum SE2Velocities in a single message.

    - - - - - + + + - - - - - + + + - - - + + + + + + + + + + + + + + + + + + -
    FieldTypeDescriptionselfright_feedbackSelfRightCommand.FeedbackFeedback for the self-right command request.
    max_velSE2VelocityIf set, limits the maximum velocity.safe_power_off_feedbackSafePowerOffCommand.FeedbackFeedback for the safe power off command request.
    min_velSE2VelocityIf set, limits the minimum velocity.battery_change_pose_feedbackBatteryChangePoseCommand.FeedbackFeedback for the battery change pose command request.
    payload_estimation_feedbackPayloadEstimationCommand.FeedbackFeedback for the payload estimation command request.
    constrained_manipulation_feedbackConstrainedManipulationCommand.FeedbackFeedback for the constrained manipulation command request
    statusRobotCommandFeedbackStatus.Status

    +

    -
    -

    SE3Covariance

    -

    Represents the translation/rotation covariance of an SE3 Pose. -The 6x6 matrix can be viewed as the covariance among 6 variables:
    rx ry rz x y z
    rx rxrx rxry rxrz rxx rxy rxz
    ry ryrx ryry ryrz ryx ryy ryz
    rz rzrx rzry rzrz rzx rzy rzz
    x xrx xry xrz xx xy xz
    y yrx yry yrz yx yy yz
    z zrx zry zrz zx zy zz
    where x, y, z are translations in meters, and rx, ry, rz are rotations around -the x, y and z axes in radians.
    The matrix is symmetric, so, for example, xy = yx. \

    +
    +

    FullBodyCommand.Request

    +

    The full body request must be one of the basic command primitives.

    @@ -9390,91 +9381,55 @@

    SE3CovarianceMatrix -

    + + + - - - + + + - - - + + + - - - + + + - - - - - - - - - - - - - - - - - - + + + - - - + + + - - - + + + - - - + + + -
    Row-major order representation of the covariance matrix.stop_requestStopCommand.RequestCommand to stop the robot.
    yaw_variancedoubleVariance of the yaw component of the SE3 Pose. Warning: deprecated in 2.1. This should equal cov_rzrz, inside matrix.freeze_requestFreezeCommand.RequestCommand to freeze all joints of the robot.
    cov_xxdoubleWarning: deprecated in 2.1. Use 'matrix.'selfright_requestSelfRightCommand.RequestCommand to self-right the robot to a ready position.
    cov_xydoubleWarning: deprecated in 2.1. Use 'matrix.'safe_power_off_requestSafePowerOffCommand.RequestCommand to safely power off the robot.
    cov_xzdoubleWarning: deprecated in 2.1. Use 'matrix.'
    cov_yxdoubleWarning: deprecated in 2.1. Use 'matrix.'
    cov_yydoubleWarning: deprecated in 2.1. Use 'matrix.'
    cov_yzdoubleWarning: deprecated in 2.1. Use 'matrix.'battery_change_pose_requestBatteryChangePoseCommand.RequestCommand to put the robot in a position to easily change the battery.
    cov_zxdoubleWarning: deprecated in 2.1. Use 'matrix.'payload_estimation_requestPayloadEstimationCommand.RequestCommand to perform payload mass property estimation
    cov_zydoubleWarning: deprecated in 2.1. Use 'matrix.'constrained_manipulation_requestConstrainedManipulationCommand.RequestCommand to perform full body constrained manipulation moves
    cov_zzdoubleWarning: deprecated in 2.1. Use 'matrix.'paramsgoogle.protobuf.AnyRobot specific command parameters.

    +

    -
    -

    SE3Pose

    -

    Geometric primitive to describe 3D position and rotation.

    - - - - - - - - - - - - - - - - - - - - -
    FieldTypeDescription
    positionVec3(m)
    rotationQuaternion

    -
    -

    SE3Velocity

    -

    Geometric primitive that describes a 3D velocity through it’s linear and angular components.

    +
    +

    geometry.proto

    +

    +
    +

    Area

    +

    Represents an area in the XY plane.

    @@ -9485,21 +9440,23 @@

    SE3VelocityVec3 -

    + + + - - - + + + -
    (m/s)polygonPolygon
    angularVec3(rad/s)circleCircle

    +

    -
    -

    Vec2

    -

    Two dimensional vector primitive.

    +
    +

    Bounds

    +

    Represents bounds on a value, such that lower < value < upper. +If you do not want to specify one side of the bound, set it to +an appropriately large (or small) number.

    @@ -9510,21 +9467,21 @@

    Vec2

    - + - + -
    xlower double
    yupper double

    +

    -
    -

    Vec2Value

    -

    A 2D vector of doubles that uses wrapped values so we can tell which elements are set.

    +
    +

    Box2

    +

    Geometric primitive describing a two-dimensional box.

    @@ -9535,21 +9492,16 @@

    Vec2Valuegoogle.protobuf.DoubleValue -

    - - - - + + -
    ygoogle.protobuf.DoubleValuesizeVec2

    +

    -
    -

    Vec3

    -

    Three dimensional vector primitive.

    +
    +

    Box2WithFrame

    +

    Geometric primitive to describe a 2D box in a specific frame.

    @@ -9560,26 +9512,26 @@

    Vec3

    - - - + + + - - - + + + - - - + + + -
    xdoubleboxBox2The box is specified with width (y) and length (x), and the full box is fixed at an origin, where it's sides are along the coordinate frame's axes.
    ydoubleframe_namestringThe pose of the axis-aligned box is in 'frame_name'.
    zdoubleframe_name_tform_boxSE3PoseThe transformation of the axis-aligned box into the desired frame (specified above).

    +

    -
    -

    Vec3Value

    -

    A 3D vector of doubles that uses wrapped values so we can tell which elements are set.

    +
    +

    Box3

    +

    Geometric primitive describing a three-dimensional box.

    @@ -9590,26 +9542,16 @@

    Vec3Valuegoogle.protobuf.DoubleValue -

    - - - - - - - - - + + -
    ygoogle.protobuf.DoubleValue
    zgoogle.protobuf.DoubleValuesizeVec3

    +

    -
    -

    Volume

    -

    Represents a volume of space in an unspecified frame.

    +
    +

    Box3WithFrame

    +

    Geometric primitive to describe a 3D box in a specific frame.

    @@ -9621,46 +9563,25 @@

    VolumeVec3 -

    - - -
    Dimensions in m, centered on frame origin.

    -
    -
    -

    Wrench

    -

    Geometric primitive used to specify forces and torques.

    - - - - - - + + - - - - - + + + - - - + + + -
    FieldTypeDescriptionBox3The box width (y), length (x), and height (z) are interpreted in, and the full box is fixed at an origin, where it's sides are along the coordinate frame's axes.
    forceVec3(N)frame_namestringThe pose of the axis-aligned box is in 'frame_name'.
    torqueVec3(Nm)frame_name_tform_boxSE3PoseThe transformation of the axis-aligned box into the desired frame (specified above).

    -
    +

    -
    -

    graph_nav/graph_nav.proto

    -

    -
    -

    ClearGraphRequest

    -

    Clears the graph on the server. Also clears GraphNav’s localization to the graph. -Note that waypoint and edge snapshots may still be cached on the server after this -operation.

    +
    +

    Circle

    +

    Represents a circular 2D area.

    @@ -9671,21 +9592,24 @@

    ClearGraphRequestbosdyn.api.RequestHeader -

    + + + - - - + + + -
    Common request header.center_ptVec2
    leasebosdyn.api.LeaseThe Lease to show ownership of graph-nav service.radiusdoubleDimensions in m from center_pt.

    +

    -
    -

    ClearGraphResponse

    -

    The results of the ClearGraphRequest.

    +
    +

    CylindricalCoordinate

    +

    Cylindrical coordinates are a generalization of polar coordiates, adding a +height +axis. See (http://mathworld.wolfram.com/CylindricalCoordinates.html) for +more details.

    @@ -9696,22 +9620,63 @@

    ClearGraphResponsebosdyn.api.ResponseHeader -

    + + + - - - + + + + + + + + -
    Common response header.rdoubleRadial coordinate
    lease_use_resultbosdyn.api.LeaseUseResultDetails about how the lease was used.thetadoubleAzimuthal coordinate
    zdoubleVertical coordiante

    +

    -
    -

    DownloadEdgeSnapshotRequest

    -

    The DownloadEdgeSnapshot request asks for a specific edge snapshot id to -be downloaded. Edge snapshots contain the large sensor data stored in each edge.

    +
    +

    FrameTreeSnapshot

    +

    A frame is a named location in space.
    For example, the following frames are defined by the API: \

    +
      +
    • “body”: A frame centered on the robot’s body. \

    • +
    • “vision”: A non-moving (inertial) frame that is the robot’s best +estimate of a fixed location in the world. It is based on +both dead reckoning and visual analysis of the world. \

    • +
    • “odom”: A non-moving (inertial) frame that is based on the kinematic +odometry of the robot only.
      Additional frames are available for robot joints, sensors, and items +detected in the world. \

    • +
    +

    The FrameTreeSnapshot represents the relationships between the frames that the robot +knows about at a particular point in time. For example, with the FrameTreeSnapshot, +an API client can determine where the “body” is relative to the “vision”. \

    +

    To reduce data bandwidth, the FrameTreeSnapshot will typically contain +a small subset of all known frames. By default, all services MUST +include “vision”, “body”, and “odom” frames in the FrameTreeSnapshot, but +additional frames can also be included. For example, an Image service +would likely include the frame located at the base of the camera lens +where the picture was taken. \

    +

    Frame relationships are expressed as edges between “parent” frames and +“child” frames, with an SE3Pose indicating the pose of the “child” frame +expressed in the “child” frame. These edges are included in the edge_map +field. For example, if frame “hand” is 1m in front of the frame “shoulder”, +then the FrameTreeSnapshot might contain:
    edge_map {
    key: “hand”
    value: {
    parent_frame_name: “shoulder”
    parent_tform_child: {
    position: {
    x: 1.0
    y: 0.0
    z: 0.0
    }
    }
    }
    } \

    +

    Frame relationships can be inverted. So, to find where the “shoulder” +is in relationship the “hand”, the parent_tform_child pose in the edge +above can be inverted:
    hand_tform_shoulder = shoulder_tform_hand.inverse()
    Frame relationships can also be concatenated. If there is an additional +edge specifying the pose of the “shoulder” relative to the “body”, then +to find where the “hand” is relative to the “body” do:
    body_tform_hand = body_tform_shoulder * shoulder_tform_hand \

    +

    The two properties above reduce data size. Instead of having to send N^2 +edge_map entries to represent all relationships between N frames, +only N edge_map entries need to be sent. Clients will need to determine +the chain of edges to follow to get from one frame to another frame, +and then do inversion and concatentation to generate the appropriate pose. \

    +

    Note that all FrameTreeSnapshots are expected to be a single rooted tree. +The syntax for FrameTreeSnapshot could also support graphs with +cycles, or forests of trees - but clients should treat those as invalid +representations. \

    @@ -9722,23 +9687,15 @@

    DownloadEdgeSnapshotRequestbosdyn.api.RequestHeader -

    - - - - - + + + -
    Common request header.
    edge_snapshot_idstringID of the data associated with an edge.child_to_parent_edge_mapFrameTreeSnapshot.ChildToParentEdgeMapEntrychild_to_parent_edge_map maps the child frame name to the ParentEdge. In aggregate, this forms the tree structure.

    +

    -
    -

    DownloadEdgeSnapshotResponse

    -

    The DownloadEdgeSnapshot response streams the data of the edge snapshot id -currently being downloaded in data chunks no larger than 4MB in size. It is necessary -to stream these data to avoid overwhelming gRPC with large http requests.

    +
    +

    FrameTreeSnapshot.ChildToParentEdgeMapEntry

    @@ -9749,35 +9706,21 @@

    DownloadEdgeSnapshotResponsebosdyn.api.ResponseHeader -

    - - - - - - - - + - + - - - + + + -
    Common response header.
    statusDownloadEdgeSnapshotResponse.StatusReturn status for the request.
    edge_snapshot_idkey stringID of the snapshot associated with an edge.
    chunkbosdyn.api.DataChunkChunk of data to download. Responses are sent in sequence until the data chunk is complete. After receiving all chunks, concatenate them into a single byte string. Then, deserialize the byte string into an EdgeSnapshot object.valueFrameTreeSnapshot.ParentEdge

    +

    -
    -

    DownloadGraphRequest

    -

    The DownloadGraphRequest requests that the server send the graph (waypoints and edges) -to the client. Note that the returned Graph message contains only the topological -structure of the map, and not any large sensor data. Large sensor data should be downloaded -using DownloadWaypointSnapshotRequest and DownloadEdgeSnapshotRequest. Both snapshots and -the graph are required to exist on the server for GraphNav to localize and navigate.

    +
    +

    FrameTreeSnapshot.ParentEdge

    +

    ParentEdge represents the relationship from a child frame to a parent frame.

    @@ -9788,16 +9731,21 @@

    DownloadGraphRequest

    - - - + + + + + + + + -
    headerbosdyn.api.RequestHeaderCommon request header.parent_frame_namestringThe name of the parent frame. Must be non-empty. If parent_frame_name is not a key in edge_map, it is the root of the tree.
    parent_tform_childSE3PoseTransform representing the pose of the child frame in the parent's frame.

    +

    -
    -

    DownloadGraphResponse

    -

    The DownloadGraph response message includes the current graph on the robot.

    +
    +

    Matrix

    +

    Represents a row-major order matrix of doubles.

    @@ -9808,24 +9756,26 @@

    DownloadGraphResponse

    - - - + + + - - - + + + + + + + + -
    headerbosdyn.api.ResponseHeaderCommon request header.rowsint32
    graphGraphThe structure of the graph.colsint32
    valuesdouble

    +

    -
    -

    DownloadWaypointSnapshotRequest

    -

    The DownloadWaypointSnapshot request asks for a specific waypoint snapshot id to -be downloaded and has parameters to decrease the amount of data downloaded. After -recording a map, first call the DownloadGraph RPC. Then, for each waypoint snapshot id, -request the waypoint snapshot from the server using the DownloadWaypointSnapshot RPC.

    +
    +

    Plane

    +

    Plane primitive, described with a point and normal.

    @@ -9836,33 +9786,43 @@

    DownloadWaypointSnapshotRequestbosdyn.api.RequestHeader -

    + + + - - - + + + + +
    Common request header.pointVec3A point on the plane.
    waypoint_snapshot_idstringID of the snapshot associated with a waypoint.normalVec3The direction of the planes normal.

    +
    +
    +

    PolyLine

    +

    Multi-part, 1D line segments defined by a series of points.

    + + - - - + + + + + - - - + + + -
    download_imagesboolIf true, download the full images and point clouds from each camera.FieldTypeDescription
    compress_point_cloudboolIf true, the point cloud will be compressed using the smallest available point cloud encoding. If false, three 32-bit floats will be used per point.pointsVec2

    +

    -
    -

    DownloadWaypointSnapshotResponse

    -

    The DownloadWaypointSnapshot response streams the data of the waypoint snapshot id -currently being downloaded in data chunks no larger than 4MB in size. It is necessary -to stream these data to avoid overwhelming gRPC with large http requests.

    +
    +

    Polygon

    +

    Polygon in the XY plane. +May be concave, but should not self-intersect. Vertices can be specified in either +clockwise or counterclockwise orders.

    @@ -9873,33 +9833,47 @@

    DownloadWaypointSnapshotResponsebosdyn.api.ResponseHeader -

    + + + + +
    Common response header.vertexesVec2

    +
    +
    +

    PolygonWithExclusions

    +

    Represents a region in the XY plane that consists of a single polygon +from which polygons representing exclusion areas may be subtracted.

    +

    A point is considered to be inside the region if it is inside the inclusion +polygon and not inside any of the exclusion polygons.

    +

    Note that while this can be used to represent a polygon with holes, that +exclusions are not necessarily holes: An exclusion polygon may not be +completely inside the inclusion polygon.

    + + - - - + + + + + - - - + + + - - - + + + -
    statusDownloadWaypointSnapshotResponse.StatusReturn status for the request.FieldTypeDescription
    waypoint_snapshot_idstringID of the snapshot associated with a waypoint.inclusionPolygon
    chunkbosdyn.api.DataChunkChunk of data to download. Responses are sent in sequence until the data chunk is complete. After receiving all chunks, concatenate them into a single byte string. Then, deserialize the byte string into a WaypointSnapshot object.exclusionsPolygon

    +

    -
    -

    GetLocalizationStateRequest

    -

    The GetLocalizationState request message requests the current localization state and any other -live data from the robot if desired. The localization consists of a waypoint ID and the relative -pose of the robot with respect to that waypoint.

    +
    +

    Quad

    +

    A square oriented in 3D space.

    @@ -9910,52 +9884,56 @@

    GetLocalizationStateRequestbosdyn.api.RequestHeader -

    + + + - - - + + + + +
    Common request header.poseSE3PoseThe center of the quad and the orientation of the normal. The normal axis is [0, 0, 1].
    waypoint_idstringReturn the localization relative to this waypoint, if specified.sizedoubleThe side length of the quad.

    +
    +
    +

    Quaternion

    +

    Quaternion primitive. A quaternion can be used to describe the rotation.

    + + - - - - - - - - + + + + + - - - + + + - - - + + + - - - + + + - - - + + + -
    request_live_point_cloudboolIf true, request the live edge-segmented point cloud that was used to generate this localization.
    request_live_imagesboolIf true, request the live images from realsense cameras at the time of localization.FieldTypeDescription
    request_live_terrain_mapsboolIf true, request the live terrain maps at the time of localization.xdouble
    request_live_world_objectsboolIf true, reqeuest the live world objects at the time of localization.ydouble
    request_live_robot_stateboolIf true, requests the full live robot state at the time of localization.zdouble
    compress_live_point_cloudboolIf true, the smallest available encoding will be used for the live point cloud data. If false, three 32 bit floats will be used per point in the point cloud.wdouble

    +

    -
    -

    GetLocalizationStateResponse

    -

    The GetLocalizationState response message returns the current localization and robot state, as well -as any requested live data information.

    +
    +

    Ray

    +

    A ray in 3D space.

    @@ -9966,46 +9944,46 @@

    GetLocalizationStateResponsebosdyn.api.ResponseHeader -

    - - - - - + + + - - - + + + + +
    Common response header.
    localizationLocalizationWhere the robot currently is. If a waypoint_id was specified in the request, this localization will be relative to that waypoint.originVec3Base of ray.
    robot_kinematicsbosdyn.api.KinematicStateRobot kinematic state at time of localization.directionVec3Unit vector defining the direction of the ray.

    +
    +
    +

    SE2Pose

    +

    Geometric primitive to describe 2D position and rotation.

    + + - - - + + + + + - - - + + + - - - + + + -
    remote_cloud_statusRemotePointCloudStatusStatus of one or more remote point cloud services (such as velodyne).FieldTypeDescription
    live_dataWaypointSnapshotContains live data at the time of localization, with elements only filled out if requested.positionVec2(m)
    lost_detector_stateLostDetectorStateIf the robot drives around without a good localization for a while, eventually it becomes "lost." I.E. it has a localization, but it no longer trusts that the localization it has is accurate. Lost detector state is available through this message.angledouble(rad)

    +

    -
    -

    LostDetectorState

    -

    Message describing whether or not graph nav is lost, and if it is lost, how lost it is. -If robot is lost, this state can be reset by either:

    -
      -
    • Driving to an area where the robot’s localization improves.

    • -
    • Calling SetLocalization RPC.

    • -
    +
    +

    SE2Velocity

    +

    Geometric primitive that describes a 2D velocity through it’s linear and angular components.

    @@ -10016,19 +9994,21 @@

    LostDetectorStatebool -

    + + + + + + + + -
    Whether or not the robot is currently lost. If this is true, graph nav will reject NavigateTo or NavigateRoute RPC's.linearVec2(m/s)
    angulardouble(rad/s)

    +

    - @@ -616,6 +642,7 @@

    E-Stop Endpoint Dependency

    Run Follow Fiducial Example

    Warning: by default, obstacle avoidance is disabled so that the robot can walk to exactly to the location of the tag if necessary. This means the robot will get very close to walls or any people holding April Tags if the tag remains stationary while the program continues to iterate. Obstacle avoidance can be re-enabled, however this can prevent Spot from fully achieving the goal point (of the April Tag location).

    +

    Note: this example can only be run on Windows and Linux; Mac OS is not supported explicitly.

    To run the example:

    python3 -m fiducial_follow --username USER --password PASSWORD ROBOT_IP
     
    diff --git a/docs/html/python/examples/frame_trajectory_command/README.html b/docs/html/python/examples/frame_trajectory_command/README.html index 11a9dc4c4..6b279beb9 100644 --- a/docs/html/python/examples/frame_trajectory_command/README.html +++ b/docs/html/python/examples/frame_trajectory_command/README.html @@ -8,7 +8,7 @@ - Frame Trajectory Commands — Spot 2.3.5 documentation + Frame Trajectory Commands — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -561,7 +602,8 @@ Development Kit License (20191101-BDSDK-SL). -->

    Frame Trajectory Commands

    -

    Spot’s knowledge of different frames can be used to make issuing robot commands much simpler. This example shows how to query the robot for its current position in the visual and odom frames. It then shows how to issue a trajectory command in the visual or odom frames that describes the relative motion of the body. For example, it first issues a command telling Spot to walk forward one meter, but issues the trajectory described relative to the visual frame.

    +

    Spot’s knowledge of different frames can be used to make issuing robot commands much simpler. This example shows how to query the robot for its current position in the visual and odom frames. It then shows how to issue a trajectory command in the visual or odom frames that describes the relative motion of the body. +Command line arguments are used to specify an offset to the current body’s position, which is then commanded in odom or vision frame.

    Note that trajectory commands cannot be issued in the body frame since this creates a goal point that is continually moving, so the trajectory can never reach the goal.

    See the geometry and frames documentation for more of an overview of Spot’s frames and transformation geometry.

    @@ -570,8 +612,8 @@

    E-Stop

    Run the Example

    -

    To run the example:

    -
    python3 frame_trajectory_command.py --username USER --password PASSWORD ROBOT_IP
    +

    To run the example (moving forward 1 meter):

    +
    python3 frame_trajectory_command.py --username USER --password PASSWORD ROBOT_IP --dx 1
     

    diff --git a/docs/html/python/examples/get_depth_plus_visual_image/README.html b/docs/html/python/examples/get_depth_plus_visual_image/README.html new file mode 100644 index 000000000..8ac808203 --- /dev/null +++ b/docs/html/python/examples/get_depth_plus_visual_image/README.html @@ -0,0 +1,678 @@ + + + + + + + + + + + API Example - Visualize Depth in Visual Image — Spot 3.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + + + +
    + +
    + + + + + + + + + + + + + + + + + +
    + + + + +
    +
    +
    +
    + +
    +

    API Example - Visualize Depth in Visual Image

    +

    This example demonstrates how to use the depth_in_visual_frame image sources to visualize depth in a visual image.

    +
    +

    Setup Dependencies

    +

    This example requires the bosdyn API and client to be installed, and must be run using python3. Using pip, these dependencies can be installed using:

    +
    python3 -m pip install -r requirements.txt
    +
    +
    +
    +
    +

    Running the Example

    +

    To run the example:

    +
    python3 get_depth_plus_visual_image.py --username USER --password PASSWORD ROBOT_IP
    +
    +
    +
    +
    +

    Example output

    +

    Example image

    +
    +
    + + +
    + +
    + +
    +
    + +
    + +
    + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/html/python/examples/get_image/README.html b/docs/html/python/examples/get_image/README.html index 1c39693dc..1479737e8 100644 --- a/docs/html/python/examples/get_image/README.html +++ b/docs/html/python/examples/get_image/README.html @@ -8,7 +8,7 @@ - Using the Image Service — Spot 2.3.5 documentation + Using the Image Service — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -561,7 +602,7 @@ Development Kit License (20191101-BDSDK-SL). -->

    Using the Image Service

    -

    This example program demonstrates how to list the different image sources available to query. Additionally, this example program shows how to capture an image from one or more different image sources, decode the response data, and save each image locally in a file named after the image source.

    +

    This example program demonstrates how to list the different image sources available to query. Additionally, this example program shows how to capture an image from one or more different image sources, decode the response data, and either save each image locally in a file named after the image source or display it in a live preview.

    Setup Dependencies

    This example requires the bosdyn API and client to be installed, and must be run using python3. Using pip, these dependencies can be installed using:

    @@ -569,8 +610,8 @@

    Setup Dependencies -

    Running the Example

    +
    +

    Running the Get-Image Example

    The example can be used to list the available image sources, as well as query and save image data from each image source. By default, the example is configured to communicate with the robot cameras, however it can be updated to

    To run the example and query images from the base robot cameras:

    python3 get_image.py --username USER --password PASSWORD ROBOT_IP --image-sources frontleft_fisheye_image --image-sources frontleft_depth
    @@ -581,6 +622,18 @@ 

    Running the Example--image-service argument can be passed with the image service name being tested. For example, to test the web cam service, the argument --image-service web-cam-service can be included when running the example. Since the other image services will be registered with the robot’s directory service, the get_image example can be run from any computer and just needs an API connection to the robot to be able to access the external image service and its images.

    Note that the front left and front right cameras on Spot are rotated 90 degrees counterclockwise from upright, and the right camera on Spot is rotated 180 degrees from upright. As a result, the corresponding images aren’t initially saved with the same orientation as is seen on the tablet. By adding the command line argument --auto-rotate, this example code automatically rotates all images from Spot to be saved in the orientation they are seen on the tablet screen.

    +
    +

    Running the Image-Viewer Example

    +

    This example can be used to create popup windows which show a live preview of the image sources specified.

    +

    To run the example and display images from the base robot cameras:

    +
    python3 image_viewer.py --username USER --password PASSWORD ROBOT_IP --image-sources frontleft_fisheye_image
    +
    +
    +

    The command specifies each source from which images should be captured using the command line argument --image-sources, exactly like the Get-Image example. As well, the arguments --image-service and --auto-rotate are used identically as described in the section above for the Get-Image example.

    +

    The argument --jpeg-quality-percent can be provided to change the JPEG quality of the requested images; this argument describes a percentage (0-100) for the quality.

    +

    The argument --capture-delay can be used to change the wait time between image captures in milliseconds.

    +

    If only a single image source is requested to be displayed, by default the program will make the image viewer show a full screen stream. To disable this, provide the argument --disable-full-screen, which will make the image stream display auto-size to approximately the size of the image.

    +

    diff --git a/docs/html/python/examples/get_mission_state/README.html b/docs/html/python/examples/get_mission_state/README.html index f962c37c0..eca7636e1 100644 --- a/docs/html/python/examples/get_mission_state/README.html +++ b/docs/html/python/examples/get_mission_state/README.html @@ -8,7 +8,7 @@ - Retrieving Mission state — Spot 2.3.5 documentation + Retrieving Mission state — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/get_robot_state/README.html b/docs/html/python/examples/get_robot_state/README.html index 6568b572d..97c65a368 100644 --- a/docs/html/python/examples/get_robot_state/README.html +++ b/docs/html/python/examples/get_robot_state/README.html @@ -8,7 +8,7 @@ - Using the Robot State Service — Spot 2.3.5 documentation + Using the Robot State Service — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/get_robot_state_async/README.html b/docs/html/python/examples/get_robot_state_async/README.html index 8d7adde4c..3536f2614 100644 --- a/docs/html/python/examples/get_robot_state_async/README.html +++ b/docs/html/python/examples/get_robot_state_async/README.html @@ -8,7 +8,7 @@ - Performing Asynchronous State Queries on Spot — Spot 2.3.5 documentation + Performing Asynchronous State Queries on Spot — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/get_world_objects/README.html b/docs/html/python/examples/get_world_objects/README.html index 64bc76f84..cbc2f1958 100644 --- a/docs/html/python/examples/get_world_objects/README.html +++ b/docs/html/python/examples/get_world_objects/README.html @@ -8,7 +8,7 @@ - Using the World Object Service — Spot 2.3.5 documentation + Using the World Object Service — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/graph_nav_anchoring_optimization/README.html b/docs/html/python/examples/graph_nav_anchoring_optimization/README.html new file mode 100644 index 000000000..da8f2f427 --- /dev/null +++ b/docs/html/python/examples/graph_nav_anchoring_optimization/README.html @@ -0,0 +1,827 @@ + + + + + + + + + + + Graph Nav Anchoring Optimization Example — Spot 3.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + + + +
    + +
    + + + + + + + + + + + + + + + + + +
    + + + + +
    +
    +
    +
    + +
    +

    Graph Nav Anchoring Optimization Example

    +

    This example demonstrates how to use the map processing service to align a graph nav map to a blueprint. This assumes that you have a running robot connected to the client.

    +
    +
    +

    Setup Dependencies

    +

    These examples require the bosdyn API and client to be installed, and must be run using python3. Using pip, these dependencies can be installed using:

    +
    python3 -m pip install -r requirements.txt
    +
    +
    +

    The example also requires matplotlib. Depending on your system you may need to set up a backend for it to display properly. One possible backend to use is Qt5: python3 -m pip install pyqt5 and set the environment variable MPLBACKEND to qt5agg.

    +
    +
    +

    Running the Example

    +

    Run the example using:

    +
    python3 -m graph_nav_anchoring_optimization --username USER --password PASSWORD ROBOT_IP
    +
    +
    +

    This will load the example map from the data directory, upload it to your robot, and then align it to the provided blueprint.

    +

    When the example has finished running, it will display an image. The image shows a blueprint. Drawn on top of the blueprint there will be a series of red lines and a series of green lines.

    +

    The red lines are the anchoring of the map before optimization (this is the default anchoring). The green lines are the anchoring of the map after optimization. The green lines should line up with the hallway in the middle of the blueprint.

    +

    After the example runs, a new map will have been saved to the data folder containing the optimized anchoring associated with the blueprint.

    +
    +
    +

    Understanding the Example Code

    +
    +

    Background on Anchorings and Metric Consistency

    +

    Graph Nav maps are a collection of waypoints and edges. Waypoints define named locations in the world, and edges define how to get from one waypoint to another. Normally, there is no requirement that Graph Nav maps have what is called “metric accuracy,” or “metric consistency.” That is, there is actually no fixed reference frame that Graph Nav maps can be displayed in.

    +

    For example, let’s suppose we have three waypoints w1 and w2, and w3 connected by the edges (w1, w2), (w2, w3) and (w1, w3). Topologically, this is a triangle:

    +
    w1 - w2
    +  \  |
    +    w3
    +
    +
    +

    Now, let’s suppose the edge (w1, w2) is defined as

    +
    from_tform_to = (x = 1, y = 0, z = 0, rotation = identity)
    +
    +
    +

    and let’s suppose the edge (w1, w3) is:

    +
    from_tform_to = (x = 1, y = 1, z = 0, rotation = identity)
    +
    +
    +

    And (w2, w3) is:

    +
    from_tform_to = (x = -0.1, y = 1.5, z = 0, rotation = identity)
    +
    +
    +

    Now, let’s suppose we want to determine where all the waypoints are in some fixed reference frame. If we only know about the edge transformations, and arbitrarily assign w1 to be the origin of our fixed reference frame, we can follow w1 through (w1, w2) to determine that (w2) is at x=1, y=0, z=0. But what about w3? The answer actually depends on whether we take the path through (w2, w3) or (w1, w3)!

    +

    If we take the first path, we would find that w3’s coordinates are x=0.9, y=1.5. If we take the second path, we find that w3’s coordinates are x = 1, y = 1. This is because the graph shown above is metrically inconsistent.

    +

    Graph Nav maps normally become metrically inconsistent due to odometry drift and inaccurate measurements between waypoints. This is normally okay – the robot can tolerate a large amount of metric inconsistency while localizing and navigating. However, if you wish to use a Graph Nav map for visualization or creating a high quality map, or registering to existing data, metric inconsistency can make this task very difficult.

    +

    To solve this problem, Graph Nav provides a concept called anchorings. Anchorings are a mapping from waypoint to its pose in a metrically consistent reference frame. A graph may have many anchorings, for example to a blueprint, BIM model, or point cloud. By providing an anchoring to a graph nav graph, you can more easily display and manipulate Graph Nav maps for your specific application.

    +
    +
    +

    Anchoring Optimization

    +

    The Map Processing Service can be used to find metrically consistent anchorings using anchoring optimization, and can be used to align Graph Nav maps to other data sources such as blueprints.

    +

    In this example, we will show how to use the Anchoring Optimization Service to align graph nav maps to a blueprint. Graph Nav maps can be aligned to any data source so long as we have good guesses for where either an April Tag or a specific waypoint is with respect to that data. In this example, we will align an April Tag to a blueprint, and use that as a hint for anchoring optimization – but you could also align individual waypoints to a blueprint, or use another data source such as a digital twin or BIM model.

    +
    +

    Step 1: setting up a connection to the robot

    +

    The Map Processing Service runs on the robot. Therefore, we will need a connection to the robot, and a lease.

    +
        # Setup and authenticate the robot.
    +    sdk = bosdyn.client.create_standard_sdk('Anchoring Optimization Example')
    +    robot = sdk.create_robot(options.hostname)
    +    robot.authenticate(options.username, options.password)
    +    _lease_client = robot.ensure_client(LeaseClient.default_service_name)
    +
    +    # We need a lease for the robot to access the map services. This prevents multiple
    +    # clients from fighting over the map data.
    +    _lease_wallet = _lease_client.lease_wallet
    +    _lease = _lease_client.acquire()
    +    _lease_keepalive = LeaseKeepAlive(_lease_client)
    +
    +    # Create clients for graph nav and map processing.
    +    graph_nav_client = robot.ensure_client(GraphNavClient.default_service_name)
    +    map_processing_client = robot.ensure_client(MapProcessingServiceClient.default_service_name)
    +
    +
    +
    +
    +

    Step 2: loading and uploading a graph_nav graph and data

    +

    The map processing service requires us to upload a graph nav graph and associated snapshot data. The service uses these data to create a metrically consistent anchoring. Maps are stored in the graph nav service, which requires a graph nav client connection. Once inside the graph nav service, maps are accessible to the map processing service.

    +
        # Load the graph from the disk and upload it to the robot.
    +    (graph, waypoint_snapshots, edge_snapshots) = load_graph_and_snapshots(options.input_map)
    +    upload_graph_and_snapshots(graph_nav_client, graph, waypoint_snapshots, edge_snapshots)
    +
    +
    +
    +
    +

    Step 3: Defining the optimization problem

    +

    Now that we have a connection to the robot and have loaded the graph and snapshots, we can tell the map processing service to optimize the graph’s anchoring.

    +

    We can provide parameters for the optimizer and hints. Parameters control how many iterations the optimizer will run, and what data sources it will use for optimization. If no parameters are provided, the optimizer will use reasonable defaults. Hints tell the optimizer information about the anchoring – for example where a particular April Tag is, or a particular waypoint. If we provide no hints at all, the Map Processing Service will choose an arbitrary waypoint to be the origin.

    +

    In this case, we will provide a single hint to the service – the location of a fiducial (April Tag).

    +
    def optimize_anchoring(opt_info, client):
    +    """
    +    Sends an RPC to the robot which optimizes the anchoring and links it to the position of the
    +    fiducial in the blueprint.
    +    :param opt_info: info needed for the optimization.
    +    :param client: the map processing client.
    +    :return: the response to the process_anchoring rpc.
    +    """
    +    initial_hint = map_processing_pb2.AnchoringHint()
    +    object_hint = initial_hint.world_objects.add()
    +    object_hint.object_anchor.id = str(opt_info.fiducial_id)
    +    object_hint.object_anchor.seed_tform_object.CopyFrom(opt_info.get_fiducial_origin())
    +
    +
    +

    To get the location of a fiducial, we start with a blueprint image (an example is provided in this example at data/house_plans.png). We need to know the relationship between pixels and meters in the image. In this case, the blueprint provides a helpful ruler that tells us the scale – approximately 49.2 pixels per meter. Once we know this, and we know the location of the fiducial on the blueprint, we can calculate the pose of the fiducial in our desired anchoring frame. NOTE: we will assume that the fiducial is mounted vertically against a wall, with the fiducial “number” upright.

    +
    
    +    def get_fiducial_origin(self):
    +        """
    +        Get an SE3Pose proto defining the origin of the fiducial in the world frame.
    +        The world frame starts at the bottom left of the image, with positive y up, positive x
    +        to the right, and positive z out of the page.
    +        :return: the SE3Pose proto defining the fiducial in this origin.
    +        """
    +        theta = np.deg2rad(self.fiducial_rotation)
    +        # Fiducial frame:
    +        # Assume x is up, and z points out. The rotation matrix
    +        # therefore has x pointed directly out of the page, and
    +        # the zy vectors pointing to the left and up respectively.
    +        # Note that the image origin has z pointing out of the page,
    +        # y up and x to the right.
    +        # Therefore the z axis is equal to (cos(t), sin(t)) and the y axis is
    +        #  (sin(t), -cos(t)).
    +        rot_matrix = np.array([[0, np.sin(theta), np.cos(theta)],
    +                               [0, -np.cos(theta),  np.sin(theta)],
    +                               [1, 0, 0]])
    +        world_tform_fiducial = SE3Pose(rot=Quat.from_matrix(rot_matrix),
    +                                       x=self.fiducial_position[0]/self.pixels_per_meter,
    +                                       y=self.fiducial_position[1]/self.pixels_per_meter,
    +                                       z=0)
    +
    +
    +

    By convention, we will assume that the origin of the anchoring is the bottom left of the image, and that the x axis is to the right, with the y axis up. We will also assume the z height of the fiducial is fixed at z = 0. From there, we can determine the position and orientation of the fiducial in 3D space w.r.t the anchoring.

    +

    We pass this in as an initial hint to the anchoring optimizer, which it will use to align our map to the blueprint (and to ensure that it is metrically consistent).

    +
    +
    +
    +

    Running the optimization and interpreting the results

    +

    We can now send a ProcessAnchoringRequest to the Map Processing Service with our initial guess, and get a result back.

    +
        return client.process_anchoring(params=map_processing_pb2.ProcessAnchoringRequest.Params(
    +                                        optimize_existing_anchoring=BoolValue(value=False)
    +                                    ),
    +                                    modify_anchoring_on_server=False,
    +                                    stream_intermediate_results=False,
    +                                    initial_hint=initial_hint)
    +
    +
    +

    We will choose not to optimize_existing_anchoring, modify_anchoring_on_server or stream_intermediate_results in this example. modify_anchoring_on_server changes the anchoring that the robot has internally, optimize_existing_anchoring uses the anchoring on the server as an initial guess, and stream_intermediate_results will send back partial results at each iteration of the optimization for debugging and visualization purposes.

    +

    The type of the result is ProcessAnchoringResponse. It has a status code, number of iterations, and final cost. If the optimizer failed, or the initial hints were malformed, the optimizer will return a failed status code with some information about why it failed.

    +

    If optimization succeeds the optimizer returns a new Anchoring.

    +
        # Extract the anchoring from the RPC response.
    +    optimized_anchoring = map_pb2.Anchoring()
    +    for wp in anchoring_response.waypoint_results:
    +        optimized_anchoring.anchors.add().CopyFrom(wp)
    +    for obj in anchoring_response.world_object_results:
    +        optimized_anchoring.objects.add().CopyFrom(obj)
    +
    +
    +

    As we can see, an Anchoring just consists of a set of waypoints and world objects (for the time being, just April Tags), and the optimized SE3Pose of those waypoints and objects in the anchoring reference frame (in this case, the position/orientation with respect to the lower left corner of the blueprint image).

    +

    We can now draw the anchorings on the blueprint using matplotlib.

    +

    The image in data/optimized_anchoring.png shows the anchoring before optimization (red), and after (green) as a set of lines. Each line is just a line between individual waypoints in the graph which have an edge between them. The fiducial is also shown as two axes, its z axis (blue) and its y axis (green). We can see it is on the closet door of the upper left bedroom.

    +

    Note that in the optimized anchoring, the apparent path of the robot is from the upper left bedroom into the living room on the lower right, and then back.

    +
    +
    +

    Other ways of viewing anchorings

    +

    The view_map.py example now takes in an argument -a, which can be used to draw a map in its anchoring frame. We can also draw the newly optimized map in the anchoring frame after saving it by calling from this directory:

    +

    view_map -a ./data/blueprint_example_optimized.walk

    +

    An example can be seen in the image stored in this example: data/optimized_anchoring_viewer.png, where we can see the point clouds of the map drawn in the anchoring frame.

    +

    When -a is not passed as an argument, the map is shown by chaining edges together starting from an arbitrary origin. As discussed in the first section, this results in an inconsistent drawing.

    +

    The difference is subtle – in the unoptimized map, we can see that there is significant height drift between the robot’s initial path from the upper left bedroom to the living room and back. This is made most apparent by looking at fiducial 319, which appears in multiple places (with different heights) depending on which waypoint is observing it. In the optimized anchoring, this drift is totally corrected.

    +
    +
    + + +
    + +
    + +
    +
    + +
    + +
    + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/html/python/examples/graph_nav_command_line/README.html b/docs/html/python/examples/graph_nav_command_line/README.html index 6c2a10503..70779c3d9 100644 --- a/docs/html/python/examples/graph_nav_command_line/README.html +++ b/docs/html/python/examples/graph_nav_command_line/README.html @@ -8,7 +8,7 @@ - GraphNav and Recording Service Command Line Interfaces — Spot 2.3.5 documentation + GraphNav and Recording Service Command Line Interfaces — Spot 3.0.0 documentation @@ -41,8 +41,8 @@ - - + + @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/graph_nav_extract_point_cloud/README.html b/docs/html/python/examples/graph_nav_extract_point_cloud/README.html new file mode 100644 index 000000000..a7ea99dd3 --- /dev/null +++ b/docs/html/python/examples/graph_nav_extract_point_cloud/README.html @@ -0,0 +1,701 @@ + + + + + + + + + + + GraphNav Point Cloud Extractor — Spot 3.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + + + +
    + +
    + + + + + + + + + + + + + + + + + +
    + + + + +
    +
    +
    +
    + +
    +

    GraphNav Point Cloud Extractor

    +

    This is an example program for opening and parsing a GraphNav map and extracting a globally consistent point cloud from it. This requires the map to have been run through Anchoring Optimization first (see the graph_nav_anchoring_optimization example. Note that if the map was recorded using Autowalk, it will automatically meet the prerequisites). The data is expected to be in XYZ32F format.

    +
    +

    Setup Dependencies

    +

    This example requires Numpy, and requires python 3. Using pip, these dependencies can be installed using:

    +
    python3 -m pip install -r requirements.txt
    +
    +
    +
    +
    +

    Running the Example

    +
      +
    1. Record a map using AutoWalk or the Command Line interface. (If using Autowalk, transfer the map from Documents/bosdyn/autowalk/your_map.walk to your local machine using a USB cable). The map should be a directory of the form:

    2. +
    +
    - /your_map.walk
    +    + graph
    +    - waypoint_snapshots
    +    - edge_snapshots
    +
    +
    +
      +
    1. Run the point cloud extractor

    2. +
    +
    python3 -m extract_point_cloud --path <path_to_your_map_directory> --output <output PLY file>
    +
    +
    +
    +
    +

    Understanding the Point Cloud Extractor

    +

    This example

    +
      +
    1. Loads a Graph Nav Map from a directory.

    2. +
    3. Extracts the data from each waypoint.

    4. +
    5. Transforms the data into the seed frame using the anchoring of each waypoint.

    6. +
    7. Saves the data to a .PLY file.

    8. +
    +

    To view the data, use a 3D model viewer (such as MeshLab or CloudCompare).

    +
    +
    +

    What is actually in the point cloud?

    +

    For base platform robots, the data in the point cloud will be visual feature data. It will be sparse, and correspond to 3D points with high contrast near the robot. For robots with LIDAR, the data will be a combination of visual feature data and LIDAR data. LIDAR data is downsampled to a 10cm resolution, and some horizontal surfaces (e.g the floor or ceiling) are intentionally filtered out.

    +

    These data are used for localization purposes and shouldn’t be used as, for example, a “digital twin”.

    +
    +
    + + +
    + +
    + +
    +
    + +
    + +
    + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/html/python/examples/graph_nav_view_map/README.html b/docs/html/python/examples/graph_nav_view_map/README.html index aab446fe4..b118d0e86 100644 --- a/docs/html/python/examples/graph_nav_view_map/README.html +++ b/docs/html/python/examples/graph_nav_view_map/README.html @@ -8,7 +8,7 @@ - GraphNav Map Viewer — Spot 2.3.5 documentation + GraphNav Map Viewer — Spot 3.0.0 documentation @@ -42,7 +42,7 @@ - + @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/hello_spot/README.html b/docs/html/python/examples/hello_spot/README.html index d67661c0e..fb58deeb7 100644 --- a/docs/html/python/examples/hello_spot/README.html +++ b/docs/html/python/examples/hello_spot/README.html @@ -8,7 +8,7 @@ - Hello Spot — Spot 2.3.5 documentation + Hello Spot — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/logging/README.html b/docs/html/python/examples/logging/README.html index 12700bc90..16b989ab7 100644 --- a/docs/html/python/examples/logging/README.html +++ b/docs/html/python/examples/logging/README.html @@ -8,7 +8,7 @@ - Logging Through the API — Spot 2.3.5 documentation + Logging Through the API — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/mission_question_answerer/README.html b/docs/html/python/examples/mission_question_answerer/README.html index 727e9ab54..f196430d8 100644 --- a/docs/html/python/examples/mission_question_answerer/README.html +++ b/docs/html/python/examples/mission_question_answerer/README.html @@ -8,7 +8,7 @@ - Answering a Mission Question — Spot 2.3.5 documentation + Answering a Mission Question — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/mission_recorder/README.html b/docs/html/python/examples/mission_recorder/README.html index 477caeb29..f70fd0086 100644 --- a/docs/html/python/examples/mission_recorder/README.html +++ b/docs/html/python/examples/mission_recorder/README.html @@ -8,7 +8,7 @@ - Mission Recorder — Spot 2.3.5 documentation + Mission Recorder — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/network_compute_bridge/README.html b/docs/html/python/examples/network_compute_bridge/README.html index 4192df64c..48423937a 100644 --- a/docs/html/python/examples/network_compute_bridge/README.html +++ b/docs/html/python/examples/network_compute_bridge/README.html @@ -8,7 +8,7 @@ - Network Compute Bridge — Spot 2.3.5 documentation + Network Compute Bridge — Spot 3.0.0 documentation @@ -41,8 +41,8 @@ - - + + @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • -
  • Perception and World Objects Examples @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -534,7 +575,7 @@
  • Python Examples »
  • -
  • Autonomy and Missions Examples »
  • +
  • Perception & World Objects Examples »
  • Network Compute Bridge
  • @@ -578,6 +619,7 @@

    Network Compute Bridge

    Results are returned to the client. If depth data is available inside the bounding box, the robot adds depth to the result.

    +
  • Fire Extinguisher Server: Example of a network compute bridge server for detecting fire extinguishers that uses Keras Retinanet instead of TensorFlow.

  • System Diagram

    @@ -592,11 +634,11 @@

    Server
    For non-GPU installations:
     
    -python3 -m pip install -r requirements_tensorflow_server_cpu.txt
    +python3 -m pip install -r requirements_server_cpu.txt
     
     For CUDA / NVIDIA GPU installations:
     
    -python3 -m pip install -r requirements_tensorflow_server_gpu.txt
    +python3 -m pip install -r requirements_server_gpu.txt
     

    Installation of NVIDIA drivers and CUDA is outside the scope of the document. There are @@ -605,7 +647,7 @@

    Server

    Client

    The client example (identify_object.py) does not require TensorFlow:

    -
    python3 -m pip install -r requirements_client_only.txt
    +
    python3 -m pip install -r requirements_client.txt
     

    @@ -639,6 +681,44 @@

    ExecutionUSERNAME and PASSWORD are your user credentials for your Spot.

    +
    +

    Robot-independent Execution

    +

    To run this example without a robot in the pipeline, first launch the server with the “-r” flag:

    +
    python3 tensorflow_server.py -r -d <MODEL DIRECTORY>
    +
    +
    +

    After launching tensorflow_server.py, the user may post requests to it using the identify_object_without_robot.py client example. To run this example with the above example server and model, run:

    +
    python3 identify_object_without_robot.py --model <MODEL_NAME> --server <SERVER_IP:PORT> --confidence 0.5 --model frozen_inference_graph
    +
    +For example:
    +
    +python3 identify_object_without_robot.py --confidence 0.5 --model frozen_inference_graph --input-image-dir ~/Download/images/ -v
    +
    +
    +

    Example output:

    +
        Got 2 objects.
    +    name: "obj1_label_person"
    +    image_properties {
    +        coordinates {
    +        vertexes {
    +            x: 1146.0
    +            y: 27.0
    +        }
    +    --- cut ---
    +
    +
    +
    +
    +

    Docker Execution

    +

    This example contains the configuration files to run the python scripts described above also in Docker containers. The docker containers accept the same arguments descrined above. For example, to run the server_cpu docker container and the client docker container, follow the steps below with the correct values for the <> variables:

    +
    sudo docker build -t ncb_server_cpu -f Dockerfile.server_cpu .
    +sudo docker run -it --network=host -v <MODEL_DIRECTORY>:/model_dir/ ncb_server_cpu --model-dir /model_dir/ --username <USERNAME> --password <PASSWORD> <ROBOT_IP>
    +
    +sudo docker build -t ncb_client -f Dockerfile.client .
    +sudo docker run -it --network=host ncb_client --username <USERNAME> --password <PASSWORD> --service tensorflow-server --confidence 0.5 --model frozen_inference_graph --image-source frontleft_fisheye_image <ROBOT_IP>
    +
    +
    +

    Troubleshooting

    @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -561,21 +602,40 @@ Development Kit License (20191101-BDSDK-SL). -->

    Using the Payload Service

    -

    This example program demonstrates how to create a payload and register this new payload with the payload service. As well, the example program shows how to list all payloads registered with Spot’s payload service, which should include the newly registered payload created in the example.

    +

    These example programs demonstrate how to use the payload service.

    +

    The payload.py example demonstrates how to create a payload and register this new payload with the payload service, and also how to list all payloads registered with Spot’s payload service. The list should include the newly registered payload created in the example.

    +

    The attach_detach_payload.py example demonstrates how to tell the robot that a payload is attached or detached through the service. This can also be done via the webserver. This is particularly useful if you are attaching and removing payloads from the robot while the robot is working (like picking up a sensor in the gripper), and you’d like to inform the robot of the updates programmatically.

    Setup Dependencies

    -

    This example requires the bosdyn API and client to be installed, and must be run using python3. Using pip, these dependencies can be installed using:

    +

    These examples require the bosdyn API and client to be installed, and must be run using python3. Using pip, these dependencies can be installed using:

    python3 -m pip install -r requirements.txt
     
    -
    -

    Running the Example

    -

    To run the example:

    +
    +

    Running the Payload Registration Example

    +

    To run the payload registration example:

    python3 payloads.py --username USER --password PASSWORD ROBOT_IP
     
    +
    +

    Running the Attach or Detach Payload Example

    +

    A payload must be registered and authorized before it can be attached and detached over the API.

    +
      +
    1. Register a payload, e.g. using the above example. When you register the payload, save the GUID and secret that you choose for the payload. In the above example, these are set to variable names payload.GUID and payload_secret.

    2. +
    3. Use the Spot webserver to “authorize” the payload. Open up the webserver, select the ‘Payloads’ tab, then click ‘authorize’ for the payload you wish to authorize. If a payload has multiple presets associated with it, you will be prompted to select a preset before authorizing.

    4. +
    5. Run the example with one of the following calls, passing the guid and secret as arguments to the script:

    6. +
    +

    To attach the payload, run:

    +
    python3 attach_detach_payload.py ROBOT_IP --guid GUID --secret SECRET --attach 
    +
    +
    +

    To detach the payload, run:

    +
    python3 attach_detach_payload.py ROBOT_IP --guid GUID --secret SECRET --detach 
    +
    +
    +
    diff --git a/docs/html/python/examples/post_docking_callbacks/README.html b/docs/html/python/examples/post_docking_callbacks/README.html new file mode 100644 index 000000000..be4c3d150 --- /dev/null +++ b/docs/html/python/examples/post_docking_callbacks/README.html @@ -0,0 +1,729 @@ + + + + + + + + + + + Post Docking Callback Examples — Spot 3.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + + + +
    + +
    + + + + + + + + + + + + + + + + + +
    + + + + +
    +
    +
    +
    + +
    +

    Post Docking Callback Examples

    +

    The scripts in this folder allow you to upload files saved to DAQ during robot operation to various endpoints, with the target use case having the callback run when Spot docks at the end of an Autowalk mission.

    +
    +

    Install Packages

    +

    Run the below to install the necessary dependencies:

    +
    python3 -m pip install -r requirements.txt
    +
    +
    +
    +
    +

    Configuration Requirements

    +

    For AWS, you must have your config file saved at ~/.aws/config with format:

    +
    [default]
    +aws_access_key_id=KEY
    +aws_secret_access_key=KEY
    +
    +
    +

    If running on a CORE, you will need access to the internet or your local network.

    +
    +
    +

    Run a Callback

    +

    Run the scripts by the following commands: +AWS:

    +
    python3 -m daq_upload_docking_callback --destination aws --bucket-name YOUR_BUCKET --host-ip HOST_COMPUTER_IP --username USERNAME --password PASSWORD SPOT_IP
    +
    +
    +

    Note: You can either use a config file at ~/.aws/config or use the --aws-access-key and --aws-secret-key arguments to have this service create the file.

    +

    GCP:

    +
    python3 -m daq_upload_docking_callback --destination gcp --key-filepath PATH_TO_KEY_JSON --bucket-name YOUR_BUCKET --host-ip HOST_COMPUTER_IP --username USERNAME --password PASSWORD SPOT_IP
    +
    +
    +

    Local:

    +
    python3 -m daq_upload_docking_callback --destination local --destination-folder DESTINATION --host-ip HOST_COMPUTER_IP --username USERNAME --password PASSWORD SPOT_IP
    +
    +
    +

    You can use the optional --time-period argument to adjust how far back the callback should look for files. If not specified, the callback will look for files starting from when the callback was initialized. After running once, the start time will be reset.

    +
    +
    +

    Run a Callback using Docker

    +

    Please refer to this document for general instructions on how to run software applications on computation payloads as docker containers.

    +

    You can find general instructions on how to build and use the docker image here.

    +

    To build the Docker image, run:

    +
    sudo docker build -f Dockerfile  -t docking_callback .
    +sudo docker save docking_callback > docking_callback.tar
    +
    +
    +

    Note: For the AWS callback, you must copy your config file as config to this directory for docker build to work. You will then uncomment COPY ./config ~/.aws/config in Dockerfile. Alternatively, you can supply your keys by using the --aws-access-key and --aws-secret-key arguments.

    +

    Open Portainer (https://ROBOT_IP:21900), login, and navigate to “Images”.

    +

    Click “Import”, select your docking_callback.tar file, and upload.

    +

    Navigate to “Containers”, click “Add Container”, and fill out the following fields:

    +
      +
    • “Name” = Name of the container. This should be set to a unique string that describes the container.

    • +
    • “Image” = {IMAGE_NAME}:latest. {IMAGE_NAME} represents the image name used to build the docker image.

    • +
    • “Publish all exposed network ports to random host ports” = True. This reduces port conflicts.

    • +
    • Under the “Command & logging” tab in the container configuration page, add arguments depending on your configuration. On the CORE, the CORE_IP should be 192.168.50.5 and the ROBOT_IP should be 192.168.50.3.

      +
        +
      • AWS --destination aws --bucket-name YOUR_BUCKET --host-ip HOST_COMPUTER_IP --username USERNAME --password PASSWORD SPOT_IP

      • +
      • GCP --destination gcp --key-filepath PATH_TO_KEY_JSON --bucket-name YOUR_BUCKET --host-ip HOST_COMPUTER_IP --username USERNAME --password PASSWORD SPOT_IP

      • +
      • Local --destination local --destination-folder DESTINATION --host-ip HOST_COMPUTER_IP --username USERNAME --password PASSWORD SPOT_IP

      • +
      +
    • +
    • Under the “Network” tab in the container configuration page, set the “Network” field to host so ports are forwarded correctly between the host OS and the docker container.

    • +
    • Under the “Restart policy” tab in the container configuration page, set the policy to “Unless stopped”. This will allow the docker container to continue to keep running all the time (even after rebooting the spot core) unless it is manually stopped by a user in Portainer.

    • +
    +

    Click “Create”. This will build your container. To verify that the callback registered, click logs and verify that you see the expected:

    +
    DATE TIME - INFO - Started the DaqDockingUploadServicer server.
    +DATE TIME - INFO - daq-docking-upload-callback service registered/updated.
    +DATE TIME - INFO - Starting directory registration loop for daq-docking-upload-callback
    +
    +
    +
    +
    + + +
    + +
    + +
    +
    + +
    + +
    + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/html/python/examples/remote_mission_service/README.html b/docs/html/python/examples/remote_mission_service/README.html index 89e0307b7..9c17b96c0 100644 --- a/docs/html/python/examples/remote_mission_service/README.html +++ b/docs/html/python/examples/remote_mission_service/README.html @@ -8,7 +8,7 @@ - Run and Interact with a RemoteMissionService. — Spot 2.3.5 documentation + Run and Interact with a RemoteMissionService. — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/replay_mission/README.html b/docs/html/python/examples/replay_mission/README.html index cc4f8121e..4a344a1b2 100644 --- a/docs/html/python/examples/replay_mission/README.html +++ b/docs/html/python/examples/replay_mission/README.html @@ -8,7 +8,7 @@ - Replaying a Mission — Spot 2.3.5 documentation + Replaying a Mission — Spot 3.0.0 documentation @@ -41,7 +41,7 @@ - + @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -561,7 +602,7 @@ Development Kit License (20191101-BDSDK-SL). -->

    Replaying a Mission

    -

    This example shows how to replay a mission via the API. The user may pass a map directory and/or a mission file for Spot to play back.

    +

    This example shows how to replay a mission via the API. The replay_mission.py script allows the user to replay Autowalk missions using graph_nav, as well as simple missions that do not use graph_nav for navigation.

    This example assumes that Spot can see the fiducial from “Start” of your mission. To ensure the fiducial is in view, move Spot to a location where the initial fiducial is seen and verify that the fiducial has a purple overlay in the tablet camera view. You may then disconnect the tablet, engage the E-Stop on your current device, and replay the mission.

    Setup Dependencies

    @@ -572,8 +613,16 @@

    Setup Dependencies

    Run the Example

    -

    To run the example:

    -
    python3 -m replay_mission --mission <path_to_mission> --username USER --password PASSWORD ROBOT_IP
    +

    To run an Autowalk mission using the default autogenerated mission file from the tablet:

    +
    python3 -m replay_mission --username USER --password PASSWORD ROBOT_IP autowalk MAP_DIRECTORY
    +
    +
    +

    To run an Autowalk mission using a customized alternate mission file:

    +
    python3 -m replay_mission --username USER --password PASSWORD ROBOT_IP autowalk MAP_DIRECTORY --autowalk_mission MISSION_FILE
    +
    +
    +

    To run a simple non-Autowalk mission that does not use graph_nav for navigation:

    +
    python3 -m replay_mission --username USER --password PASSWORD ROBOT_IP simple MISSION_FILE
     

    diff --git a/docs/html/python/examples/ricoh_theta/README.html b/docs/html/python/examples/ricoh_theta/README.html index 1e04b6cfb..dedfa2f09 100644 --- a/docs/html/python/examples/ricoh_theta/README.html +++ b/docs/html/python/examples/ricoh_theta/README.html @@ -8,7 +8,7 @@ - Interacting with a Ricoh Theta Camera — Spot 2.3.5 documentation + Interacting with a Ricoh Theta Camera — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -639,6 +680,8 @@

    Run Image Service--theta-client argument is provided, the Ricoh Theta will be connected to in client mode. Otherwise, it will default to a direct-ip mode connection, requiring the computer running the image service script to be wirelessly connected to the Ricoh Theta’s WiFi. If using the Ricoh Theta in client mode, make sure that the ricoh_client_mode.py script has been run to create a connection to Spot’s WiFi with the correct static ip.

    The --theta-ssid argument is used to pass the SSID for the Ricoh Theta camera that will take the images and should have the .OSC removed. This will be set as the image source name for the service. Additionally, if the password for the Ricoh Theta is different from the default password of the camera, this can be provided using the --theta-password argument.

    The Ricoh Theta image service will default to only attempting to capture an image from the Ricoh Theta camera when requested with a GetImage gRPC call. To have the Ricoh Theta attempt to continuously capture images, pass the command line argument --capture-continuously. This will cause the image service to create a background thread and attempt to query the ricoh theta at a high rate. Note, the Ricoh Theta images are very high resolution and the stitching process to go from the fisheye image to the processed image is time consuming, so sometimes the continuous captures will drain the camera’s battery quickly or overwhelm the camera processors.

    +

    The --live-stream argument can be provided to use a quicker image capturing method. This will create a stream that reads from the live preview results, which can then use a different, lower quality image stitching algorithm that allows a faster, live stream return of images.

    +

    Note: using the arguments --live-stream and --capture-continuously are recommended for the best results when working with the ricoh theta through the tablet in teleop.

    Lastly, a port number for the image service can be specified using the --port argument. It is possible to bypass the port argument and allow a random port number to be selected, but it is discouraged since restarts may result in unexpected changes to a service listening on the old port. This port number will be used with the host-ip (HOST_COMPUTER_IP) to fully specify where the image service is running. This port number must be open and cannot be blocked by a local firewall, otherwise the ricoh-theta image service will be unreachable from the robot and the directory registration service.

    Ricoh Theta Image Service Configuration

    diff --git a/docs/html/python/examples/self_registration/README.html b/docs/html/python/examples/self_registration/README.html index dc70d9e99..b7de32fb9 100644 --- a/docs/html/python/examples/self_registration/README.html +++ b/docs/html/python/examples/self_registration/README.html @@ -8,7 +8,7 @@ - Payload & Service Initialization — Spot 2.3.5 documentation + Payload & Service Initialization — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/service_faults/README.html b/docs/html/python/examples/service_faults/README.html index 2f587d46c..5fbd715b5 100644 --- a/docs/html/python/examples/service_faults/README.html +++ b/docs/html/python/examples/service_faults/README.html @@ -8,7 +8,7 @@ - Handling Service Faults — Spot 2.3.5 documentation + Handling Service Faults — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/spot_cam/README.html b/docs/html/python/examples/spot_cam/README.html index 16553afd9..dc0b56d6e 100644 --- a/docs/html/python/examples/spot_cam/README.html +++ b/docs/html/python/examples/spot_cam/README.html @@ -8,7 +8,7 @@ - Spot CAM Services — Spot 2.3.5 documentation + Spot CAM Services — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -623,10 +664,26 @@

    Running the Example - Spot Detect and Follow — Spot 2.3.5 documentation + Spot Detect and Follow — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -566,6 +607,8 @@

    Spot Detect and FollowThe program is organized as three sets of Python processes communicating with the Spot robot. The process diagram is shown below. The main process communicates with the Spot robot over GRPC and constantly receives images. These images are pushed into the RAW_IMAGES_QUEUE and read by the Tensorflow processes. Those processes detect objects in the images and pushes the location onto PROCESSED_BOXES_QUEUE. The main thread then determines the location of the object and issues commands to the robot to walk towards the object.

    Process Diagram

    User Guide

    +

    This example depends on a Tensorflow model. As an example, the faster_rcnn_inception_v2_coco Tensorflow model pre-trained on COCO dataset can be obtained here. That model is not supported on Windows or MacOS. +The pre-trained models may not be good at detecting some classes when using the robot’s cameras, as the fisheye distortion, low resolution, and black and white images affect image quality. For example, pre-trained models may not perform well at detecting “sports balls” due to the lack of color. The ssd_mobilenet_v2_coco and faster_rcnn_inception_v2_coco have been tested to work well at detecting humans.

    Installation (Only if you want to run without Docker)

    To install this example on Ubuntu 18.04, follow these instructions:

    @@ -601,9 +644,11 @@

    Execution
    python3 spot_detect_and_follow.py --username USER --password PASSWORD --model-path <path_to_pb> --detection-class <integer cladd id> ROBOT_IP
    +

    The simple command to run with the default values is:

    +
    python3 spot_detect_and_follow.py --username USER --password PASSWORD --model-path <path_to_pb> --detection-class <integer class id> ROBOT_IP
     
    +

    For example, run the example with --model-path pointing to the frozen_inference_graph.pb file in the faster_rcnn_inception_v2_coco_2018_01_28 folder created from untar-ing the model file downloaded from the instructions above, and with --detection-classes argument set to 1 to detect people in the camera images.

    Running in Docker

    Please refer to this document for general instructions on how to run software applications on SpotCORE as docker containers.

    @@ -611,24 +656,21 @@

    Running in Docker
    cp /path/to/examples/spot_tensorflow_detector/tensorflow_object_detector.py .
    +
    cp ../spot_tensorflow_detector/tensorflow_object_detection.py .
     sudo docker build -t spot_detect_and_follow .
     
    -

    To run a container:

    -
    sudo docker run --gpus all -it \
    +

    To run a container, replace ROBOT_HOSTNAME and <absolute_path_to_pb> with the full path to the pb model file in the command below:

    +
    sudo docker run -it \
     --network=host \
     -v <absolute_path_to_pb>:/model.pb \
    --v <absolute_path_to_classes>:/classes.json \
     spot_detect_and_follow \
     --model-path /model.pb \
     --detection-class 1
    -<typical other arguments after "python spot_detect_and_follow.py">
     ROBOT_HOSTNAME
     
    -

    As an example, the faster_rcnn_inception_v2_coco Tensorflow model pre-trained on COCO dataset can be obtained here. Run the example with --model-path pointing to the pb file in that model and with --detection-classes argument set to 1 to detect people in the camera images. That model is not supported on Windows or MacOS. -The pre-trained models may not be good at detecting some classes when using the robot’s cameras, as the fisheye distortion, low resolution, and black and white images affect image quality. For example, pre-trained models may not perform well at detecting “sports balls” due to the lack of color. The ssd_mobilenet_v2_coco and faster_rcnn_inception_v2_coco have been tested to work well at detecting humans.

    +

    To take advantage of GPU support if it is available on your system, add --gpus all in the command above.

    diff --git a/docs/html/python/examples/spot_light/README.html b/docs/html/python/examples/spot_light/README.html index 71c25c282..8fae05dd6 100644 --- a/docs/html/python/examples/spot_light/README.html +++ b/docs/html/python/examples/spot_light/README.html @@ -8,7 +8,7 @@ - Responding to User Interaction via Light — Spot 2.3.5 documentation + Responding to User Interaction via Light — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/spot_tensorflow_detector/README.html b/docs/html/python/examples/spot_tensorflow_detector/README.html index a5a32df11..a42093442 100644 --- a/docs/html/python/examples/spot_tensorflow_detector/README.html +++ b/docs/html/python/examples/spot_tensorflow_detector/README.html @@ -8,7 +8,7 @@ - Spot Tensorflow Object Detection — Spot 2.3.5 documentation + Spot Tensorflow Object Detection — Spot 3.0.0 documentation @@ -41,7 +41,7 @@ - + @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -625,7 +666,7 @@

    Execution--number-tensorflow-processes reduces the size of this queue more quickly. Increasing the value of the argument --sleep-between-capture also reduces the size of this queue, but this option also reduces the real-time visualization of the environment around the Spot robot.

    -

    As an example, the faster_rcnn_inception_v2_coco Tensorflow model pre-trained on COCO dataset can be obtained here. Run the example with --model-path pointing to the pb file in that model and with --detection-classes argument set to 1 to detect people in the camera images. That model is not supported on Windows or MacOS.

    +

    As an example, the faster_rcnn_inception_v2_coco Tensorflow model pre-trained on COCO dataset can be obtained here. Run the example with --model-path pointing to the frozen_inference_graph.pb file in that model and with --detection-classes argument set to 1 to detect people in the camera images. That model is not supported on Windows or MacOS.

    diff --git a/docs/html/python/examples/stance/README.html b/docs/html/python/examples/stance/README.html index e661b0760..f7aa3bc7c 100644 --- a/docs/html/python/examples/stance/README.html +++ b/docs/html/python/examples/stance/README.html @@ -8,7 +8,7 @@ - Adjusting Robot Stance In Place — Spot 2.3.5 documentation + Adjusting Robot Stance In Place — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/stitch_front_images/README.html b/docs/html/python/examples/stitch_front_images/README.html index 9370c4788..b9d875927 100644 --- a/docs/html/python/examples/stitch_front_images/README.html +++ b/docs/html/python/examples/stitch_front_images/README.html @@ -8,7 +8,7 @@ - Stitch Front Spot Images Together — Spot 2.3.5 documentation + Stitch Front Spot Images Together — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/tester_programs/README.html b/docs/html/python/examples/tester_programs/README.html index f389222e0..8bc108e08 100644 --- a/docs/html/python/examples/tester_programs/README.html +++ b/docs/html/python/examples/tester_programs/README.html @@ -8,7 +8,7 @@ - Tester Programs — Spot 2.3.5 documentation + Tester Programs — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/time_sync/README.html b/docs/html/python/examples/time_sync/README.html index ff2b5041a..f804823c5 100644 --- a/docs/html/python/examples/time_sync/README.html +++ b/docs/html/python/examples/time_sync/README.html @@ -8,7 +8,7 @@ - Using the Timesync Service — Spot 2.3.5 documentation + Using the Timesync Service — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/upload_choreographed_sequence/README.html b/docs/html/python/examples/upload_choreographed_sequence/README.html index dd036a1b4..2c1e2d32c 100644 --- a/docs/html/python/examples/upload_choreographed_sequence/README.html +++ b/docs/html/python/examples/upload_choreographed_sequence/README.html @@ -8,7 +8,7 @@ - Upload and Execute Choreography Sequence — Spot 2.3.5 documentation + Upload and Execute Choreography Sequence — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/velodyne_client/README.html b/docs/html/python/examples/velodyne_client/README.html index 7007d05ef..56079d0a7 100644 --- a/docs/html/python/examples/velodyne_client/README.html +++ b/docs/html/python/examples/velodyne_client/README.html @@ -8,7 +8,7 @@ - Using the Velodyne Point Cloud Service — Spot 2.3.5 documentation + Using the Velodyne Point Cloud Service — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/visualizer/README.html b/docs/html/python/examples/visualizer/README.html index b4e4c1ddf..dd50044d4 100644 --- a/docs/html/python/examples/visualizer/README.html +++ b/docs/html/python/examples/visualizer/README.html @@ -8,7 +8,7 @@ - Basic Streaming Visualizer for API Messages — Spot 2.3.5 documentation + Basic Streaming Visualizer for API Messages — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/wasd/README.html b/docs/html/python/examples/wasd/README.html index 30dd3480c..87371cfaf 100644 --- a/docs/html/python/examples/wasd/README.html +++ b/docs/html/python/examples/wasd/README.html @@ -8,7 +8,7 @@ - Controlling the Robot with a Keyboard — Spot 2.3.5 documentation + Controlling the Robot with a Keyboard — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/web_cam_image_service/README.html b/docs/html/python/examples/web_cam_image_service/README.html index c81793518..2c7af5228 100644 --- a/docs/html/python/examples/web_cam_image_service/README.html +++ b/docs/html/python/examples/web_cam_image_service/README.html @@ -8,7 +8,7 @@ - Image Service for a Web Cam — Spot 2.3.5 documentation + Image Service for a Web Cam — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • @@ -593,6 +634,7 @@

    Example Execution--codec mjpg.

    +

    There are optional arguments to change the camera’s resolution if it is possible. The arguments --res-width and --res-height can adjust the image resolution for all captures completed by the service. If the input resolution is not achievable by the camera, the nearest/most similar resolution will be chosen and used. If no resolution is provided, the image service will use the camera’s defaults.

    Lastly, the command line argument --show-debug-info will allow a user to live-view the OpenCV output of the web cam video capture on their local computer. Only use this flag for debug purposes, as it will likely slow down the main example operation and reduce the performance of the image service.

    diff --git a/docs/html/python/examples/world_object_mutations/README.html b/docs/html/python/examples/world_object_mutations/README.html index c06abbdfe..979cd9816 100644 --- a/docs/html/python/examples/world_object_mutations/README.html +++ b/docs/html/python/examples/world_object_mutations/README.html @@ -8,7 +8,7 @@ - World Object Mutations — Spot 2.3.5 documentation + World Object Mutations — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/world_object_with_image_coordinates/README.html b/docs/html/python/examples/world_object_with_image_coordinates/README.html index df70692bd..f7f27cfe4 100644 --- a/docs/html/python/examples/world_object_with_image_coordinates/README.html +++ b/docs/html/python/examples/world_object_with_image_coordinates/README.html @@ -8,7 +8,7 @@ - Using World Object Service with Image Coordinates — Spot 2.3.5 documentation + Using World Object Service with Image Coordinates — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/python/examples/xbox_controller/README.html b/docs/html/python/examples/xbox_controller/README.html index 44494769b..0a3dde78a 100644 --- a/docs/html/python/examples/xbox_controller/README.html +++ b/docs/html/python/examples/xbox_controller/README.html @@ -8,7 +8,7 @@ - Controlling the Robot with an Xbox Controller — Spot 2.3.5 documentation + Controlling the Robot with an Xbox Controller — Spot 3.0.0 documentation @@ -69,7 +69,7 @@
    - 2.3.5 + 3.0.0
    @@ -102,6 +102,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -245,6 +271,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -268,12 +295,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -321,6 +350,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -344,6 +374,8 @@
  • Choreography
  • @@ -377,6 +409,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -403,6 +437,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -410,6 +446,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -429,6 +467,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -446,6 +485,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -463,6 +503,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/search.html b/docs/html/search.html index 76ec09283..0210d1dde 100644 --- a/docs/html/search.html +++ b/docs/html/search.html @@ -7,7 +7,7 @@ - Search — Spot 2.3.5 documentation + Search — Spot 3.0.0 documentation @@ -68,7 +68,7 @@
    - 2.3.5 + 3.0.0
    @@ -101,6 +101,7 @@
  • Geometry and Frames
  • Robot services
  • E-Stop
  • +
  • Lease
  • Developing API Services
  • Faults
  • Autonomy services
  • Choreography
  • Spot Arm
  • Robot Behavior and Commands Examples
  • Arm Command Examples
  • Logging Examples
  • Autonomy and Missions Examples
  • @@ -244,6 +270,7 @@
  • Arm Surface Contact
  • Async Tasks
  • Auth
  • +
  • Auto Return
  • BDDF
  • BDDF Download
  • Channel
  • @@ -267,12 +294,14 @@
  • Graph Nav
  • Image
  • Image Service Helpers
  • +
  • IR Enable/Disable
  • Lease
  • License
  • Local Grid
  • Log Annotation
  • Math Helpers
  • Manipulation API
  • +
  • Map Processing
  • Network Compute Bridge
  • Payload Registration
  • Payload
  • @@ -320,6 +349,7 @@
  • GRPC Reader
  • GRPC Service Reader
  • GRPC Service Writer
  • +
  • Message Reader
  • POD Series Reader
  • POD Series Writer
  • Protobuf Channel Reader
  • @@ -343,6 +373,8 @@
  • Choreography
  • @@ -376,6 +408,8 @@
  • arm_surface_contact_service.proto
  • auth.proto
  • auth_service.proto
  • +
  • auto_return/auto_return.proto
  • +
  • auto_return/auto_return_service.proto
  • basic_command.proto
  • bddf.proto
  • data_acquisition.proto
  • @@ -402,6 +436,8 @@
  • graph_nav/graph_nav.proto
  • graph_nav/graph_nav_service.proto
  • graph_nav/map.proto
  • +
  • graph_nav/map_processing.proto
  • +
  • graph_nav/map_processing_service.proto
  • graph_nav/nav.proto
  • graph_nav/recording.proto
  • graph_nav/recording_service.proto
  • @@ -409,6 +445,8 @@
  • header.proto
  • image.proto
  • image_service.proto
  • +
  • ir_enable_disable.proto
  • +
  • ir_enable_disable_service.proto
  • lease.proto
  • lease_service.proto
  • license.proto
  • @@ -428,6 +466,7 @@
  • mobility_command.proto
  • network_compute_bridge.proto
  • network_compute_bridge_service.proto
  • +
  • network_stats.proto
  • parameter.proto
  • payload.proto
  • payload_estimation.proto
  • @@ -445,6 +484,7 @@
  • robot_state.proto
  • robot_state_service.proto
  • service_fault.proto
  • +
  • sparse_features.proto
  • spot/door.proto
  • spot/door_service.proto
  • spot/robot_command.proto
  • @@ -462,6 +502,7 @@
  • spot_cam/service.proto
  • spot_cam/streamquality.proto
  • spot_cam/version.proto
  • +
  • stairs.proto
  • synchronized_command.proto
  • time_range.proto
  • time_sync.proto
  • diff --git a/docs/html/searchindex.js b/docs/html/searchindex.js index 18eedcbec..da9263612 100644 --- a/docs/html/searchindex.js +++ b/docs/html/searchindex.js @@ -1 +1 @@ -Search.setIndex({docnames:["README","choreography_protos/bosdyn/api/README","choreography_protos/bosdyn/api/choreography_reference","docs/concepts/README","docs/concepts/about_spot","docs/concepts/arm/README","docs/concepts/arm/arm_concepts","docs/concepts/arm/arm_services","docs/concepts/arm/arm_specification","docs/concepts/autonomy/README","docs/concepts/autonomy/auto_return","docs/concepts/autonomy/autonomous_navigation_code_examples","docs/concepts/autonomy/autonomous_navigation_services","docs/concepts/autonomy/components_of_autonomous_navigation","docs/concepts/autonomy/docking","docs/concepts/autonomy/graphnav_and_robot_locomotion","docs/concepts/autonomy/graphnav_map_structure","docs/concepts/autonomy/graphnav_service","docs/concepts/autonomy/graphnav_tech_summary","docs/concepts/autonomy/initialization","docs/concepts/autonomy/localization","docs/concepts/autonomy/missions_service","docs/concepts/autonomy/typical_autonomous_navigation_use_case","docs/concepts/base_services","docs/concepts/bddf","docs/concepts/choreography/README","docs/concepts/choreography/choreographer","docs/concepts/choreography/choreographer_setup","docs/concepts/choreography/choreography_service","docs/concepts/choreography/move_reference","docs/concepts/data","docs/concepts/data_acquisition_output","docs/concepts/data_acquisition_overview","docs/concepts/data_buffer_overview","docs/concepts/developing_api_services","docs/concepts/estop_service","docs/concepts/faults","docs/concepts/geometry_and_frames","docs/concepts/network_compute_bridge","docs/concepts/networking","docs/concepts/robot_services","docs/concepts/writing_services_for_data_acquisition","docs/payload/README","docs/payload/configuring_payload_software","docs/payload/docker_containers","docs/payload/guidelines_for_robust_payload_design","docs/payload/mechanical_interfaces","docs/payload/payload_configuration_requirements","docs/payload/robot_electrical_interface","docs/payload/robot_mounting_rails","docs/payload/spot_core_cockpit","docs/payload/spot_core_vnc","docs/protos/README","docs/protos/style_guide","docs/python/README","docs/python/fetch_tutorial/fetch1","docs/python/fetch_tutorial/fetch2","docs/python/fetch_tutorial/fetch3","docs/python/fetch_tutorial/fetch4","docs/python/fetch_tutorial/fetch5","docs/python/quickstart","docs/python/understanding_spot_programming","docs/release_notes","protos/bosdyn/api/README","protos/bosdyn/api/proto_reference","python/README","python/bosdyn-choreography-client/src/bosdyn/choreography/client/README","python/bosdyn-choreography-client/src/bosdyn/choreography/client/choreography","python/bosdyn-client/src/bosdyn/client/README","python/bosdyn-client/src/bosdyn/client/arm_surface_contact","python/bosdyn-client/src/bosdyn/client/async_tasks","python/bosdyn-client/src/bosdyn/client/auth","python/bosdyn-client/src/bosdyn/client/bddf","python/bosdyn-client/src/bosdyn/client/bddf_download","python/bosdyn-client/src/bosdyn/client/channel","python/bosdyn-client/src/bosdyn/client/command_line","python/bosdyn-client/src/bosdyn/client/common","python/bosdyn-client/src/bosdyn/client/data_acquisition","python/bosdyn-client/src/bosdyn/client/data_acquisition_helpers","python/bosdyn-client/src/bosdyn/client/data_acquisition_plugin","python/bosdyn-client/src/bosdyn/client/data_acquisition_plugin_service","python/bosdyn-client/src/bosdyn/client/data_acquisition_store","python/bosdyn-client/src/bosdyn/client/data_buffer","python/bosdyn-client/src/bosdyn/client/data_service","python/bosdyn-client/src/bosdyn/client/directory","python/bosdyn-client/src/bosdyn/client/directory_registration","python/bosdyn-client/src/bosdyn/client/docking","python/bosdyn-client/src/bosdyn/client/door","python/bosdyn-client/src/bosdyn/client/estop","python/bosdyn-client/src/bosdyn/client/exceptions","python/bosdyn-client/src/bosdyn/client/fault","python/bosdyn-client/src/bosdyn/client/frame_helpers","python/bosdyn-client/src/bosdyn/client/graph_nav","python/bosdyn-client/src/bosdyn/client/image","python/bosdyn-client/src/bosdyn/client/image_service_helpers","python/bosdyn-client/src/bosdyn/client/lease","python/bosdyn-client/src/bosdyn/client/license","python/bosdyn-client/src/bosdyn/client/local_grid","python/bosdyn-client/src/bosdyn/client/log_annotation","python/bosdyn-client/src/bosdyn/client/manipulation_api_client","python/bosdyn-client/src/bosdyn/client/math_helpers","python/bosdyn-client/src/bosdyn/client/network_compute_bridge_client","python/bosdyn-client/src/bosdyn/client/payload","python/bosdyn-client/src/bosdyn/client/payload_registration","python/bosdyn-client/src/bosdyn/client/point_cloud","python/bosdyn-client/src/bosdyn/client/power","python/bosdyn-client/src/bosdyn/client/processors","python/bosdyn-client/src/bosdyn/client/recording","python/bosdyn-client/src/bosdyn/client/robot","python/bosdyn-client/src/bosdyn/client/robot_command","python/bosdyn-client/src/bosdyn/client/robot_id","python/bosdyn-client/src/bosdyn/client/robot_state","python/bosdyn-client/src/bosdyn/client/sdk","python/bosdyn-client/src/bosdyn/client/server_util","python/bosdyn-client/src/bosdyn/client/spot_cam/README","python/bosdyn-client/src/bosdyn/client/spot_cam/audio","python/bosdyn-client/src/bosdyn/client/spot_cam/compositor","python/bosdyn-client/src/bosdyn/client/spot_cam/health","python/bosdyn-client/src/bosdyn/client/spot_cam/lighting","python/bosdyn-client/src/bosdyn/client/spot_cam/media_log","python/bosdyn-client/src/bosdyn/client/spot_cam/network","python/bosdyn-client/src/bosdyn/client/spot_cam/power","python/bosdyn-client/src/bosdyn/client/spot_cam/ptz","python/bosdyn-client/src/bosdyn/client/spot_cam/streamquality","python/bosdyn-client/src/bosdyn/client/spot_cam/version","python/bosdyn-client/src/bosdyn/client/spot_check","python/bosdyn-client/src/bosdyn/client/time_sync","python/bosdyn-client/src/bosdyn/client/token_cache","python/bosdyn-client/src/bosdyn/client/token_manager","python/bosdyn-client/src/bosdyn/client/util","python/bosdyn-client/src/bosdyn/client/world_object","python/bosdyn-core/src/bosdyn/README","python/bosdyn-core/src/bosdyn/bddf/README","python/bosdyn-core/src/bosdyn/bddf/base_data_reader","python/bosdyn-core/src/bosdyn/bddf/block_writer","python/bosdyn-core/src/bosdyn/bddf/bosdyn","python/bosdyn-core/src/bosdyn/bddf/common","python/bosdyn-core/src/bosdyn/bddf/data_reader","python/bosdyn-core/src/bosdyn/bddf/data_writer","python/bosdyn-core/src/bosdyn/bddf/file_indexer","python/bosdyn-core/src/bosdyn/bddf/grpc_proto_reader","python/bosdyn-core/src/bosdyn/bddf/grpc_reader","python/bosdyn-core/src/bosdyn/bddf/grpc_service_reader","python/bosdyn-core/src/bosdyn/bddf/grpc_service_writer","python/bosdyn-core/src/bosdyn/bddf/pod_series_reader","python/bosdyn-core/src/bosdyn/bddf/pod_series_writer","python/bosdyn-core/src/bosdyn/bddf/protobuf_channel_reader","python/bosdyn-core/src/bosdyn/bddf/protobuf_reader","python/bosdyn-core/src/bosdyn/bddf/protobuf_series_writer","python/bosdyn-core/src/bosdyn/bddf/stream_data_reader","python/bosdyn-core/src/bosdyn/geometry","python/bosdyn-core/src/bosdyn/util","python/bosdyn-mission/src/bosdyn/mission/README","python/bosdyn-mission/src/bosdyn/mission/client","python/bosdyn-mission/src/bosdyn/mission/constants","python/bosdyn-mission/src/bosdyn/mission/exceptions","python/bosdyn-mission/src/bosdyn/mission/remote_client","python/bosdyn-mission/src/bosdyn/mission/server_util","python/bosdyn-mission/src/bosdyn/mission/util","python/examples/README","python/examples/arm_and_mobility_command/README","python/examples/arm_door/README","python/examples/arm_force_control/README","python/examples/arm_gaze/README","python/examples/arm_gcode/README","python/examples/arm_grasp/README","python/examples/arm_joint_move/README","python/examples/arm_simple/README","python/examples/arm_stow_unstow/README","python/examples/arm_surface_contact/README","python/examples/arm_trajectory/README","python/examples/arm_walk_to_object/README","python/examples/arm_with_body_follow/README","python/examples/bddf_download/README","python/examples/cloud_upload/README","python/examples/comms_test/README","python/examples/data_acquisition_service/README","python/examples/data_service/README","python/examples/directory/README","python/examples/docking/README","python/examples/docs/arm_examples","python/examples/docs/autonomy_and_missions_examples","python/examples/docs/basic_service_examples","python/examples/docs/data_acquisition_examples","python/examples/docs/logging_examples","python/examples/docs/payloads_examples","python/examples/docs/perception_world_objects_examples","python/examples/docs/robot_behavior_examples","python/examples/estop/README","python/examples/fiducial_follow/README","python/examples/frame_trajectory_command/README","python/examples/get_image/README","python/examples/get_mission_state/README","python/examples/get_robot_state/README","python/examples/get_robot_state_async/README","python/examples/get_world_objects/README","python/examples/graph_nav_command_line/README","python/examples/graph_nav_view_map/README","python/examples/hello_spot/README","python/examples/logging/README","python/examples/mission_question_answerer/README","python/examples/mission_recorder/README","python/examples/network_compute_bridge/README","python/examples/payloads/README","python/examples/remote_mission_service/README","python/examples/replay_mission/README","python/examples/ricoh_theta/README","python/examples/self_registration/README","python/examples/service_faults/README","python/examples/spot_cam/README","python/examples/spot_detect_and_follow/README","python/examples/spot_light/README","python/examples/spot_tensorflow_detector/README","python/examples/stance/README","python/examples/stitch_front_images/README","python/examples/tester_programs/README","python/examples/time_sync/README","python/examples/upload_choreographed_sequence/README","python/examples/velodyne_client/README","python/examples/visualizer/README","python/examples/wasd/README","python/examples/web_cam_image_service/README","python/examples/world_object_mutations/README","python/examples/world_object_with_image_coordinates/README","python/examples/xbox_controller/README"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":3,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":2,"sphinx.domains.rst":2,"sphinx.domains.std":1,"sphinx.ext.intersphinx":1,"sphinx.ext.viewcode":1,sphinx:56},filenames:["README.md","choreography_protos/bosdyn/api/README.md","choreography_protos/bosdyn/api/choreography_reference.md","docs/concepts/README.md","docs/concepts/about_spot.md","docs/concepts/arm/README.md","docs/concepts/arm/arm_concepts.md","docs/concepts/arm/arm_services.md","docs/concepts/arm/arm_specification.md","docs/concepts/autonomy/README.md","docs/concepts/autonomy/auto_return.md","docs/concepts/autonomy/autonomous_navigation_code_examples.md","docs/concepts/autonomy/autonomous_navigation_services.md","docs/concepts/autonomy/components_of_autonomous_navigation.md","docs/concepts/autonomy/docking.md","docs/concepts/autonomy/graphnav_and_robot_locomotion.md","docs/concepts/autonomy/graphnav_map_structure.md","docs/concepts/autonomy/graphnav_service.md","docs/concepts/autonomy/graphnav_tech_summary.md","docs/concepts/autonomy/initialization.md","docs/concepts/autonomy/localization.md","docs/concepts/autonomy/missions_service.md","docs/concepts/autonomy/typical_autonomous_navigation_use_case.md","docs/concepts/base_services.md","docs/concepts/bddf.md","docs/concepts/choreography/README.md","docs/concepts/choreography/choreographer.md","docs/concepts/choreography/choreographer_setup.md","docs/concepts/choreography/choreography_service.md","docs/concepts/choreography/move_reference.md","docs/concepts/data.md","docs/concepts/data_acquisition_output.md","docs/concepts/data_acquisition_overview.md","docs/concepts/data_buffer_overview.md","docs/concepts/developing_api_services.md","docs/concepts/estop_service.md","docs/concepts/faults.md","docs/concepts/geometry_and_frames.md","docs/concepts/network_compute_bridge.md","docs/concepts/networking.md","docs/concepts/robot_services.md","docs/concepts/writing_services_for_data_acquisition.md","docs/payload/README.md","docs/payload/configuring_payload_software.md","docs/payload/docker_containers.md","docs/payload/guidelines_for_robust_payload_design.md","docs/payload/mechanical_interfaces.md","docs/payload/payload_configuration_requirements.md","docs/payload/robot_electrical_interface.md","docs/payload/robot_mounting_rails.md","docs/payload/spot_core_cockpit.md","docs/payload/spot_core_vnc.md","docs/protos/README.md","docs/protos/style_guide.md","docs/python/README.md","docs/python/fetch_tutorial/fetch1.md","docs/python/fetch_tutorial/fetch2.md","docs/python/fetch_tutorial/fetch3.md","docs/python/fetch_tutorial/fetch4.md","docs/python/fetch_tutorial/fetch5.md","docs/python/quickstart.md","docs/python/understanding_spot_programming.md","docs/release_notes.md","protos/bosdyn/api/README.md","protos/bosdyn/api/proto_reference.md","python/README.md","python/bosdyn-choreography-client/src/bosdyn/choreography/client/README.md","python/bosdyn-choreography-client/src/bosdyn/choreography/client/choreography.rst","python/bosdyn-client/src/bosdyn/client/README.md","python/bosdyn-client/src/bosdyn/client/arm_surface_contact.rst","python/bosdyn-client/src/bosdyn/client/async_tasks.rst","python/bosdyn-client/src/bosdyn/client/auth.rst","python/bosdyn-client/src/bosdyn/client/bddf.rst","python/bosdyn-client/src/bosdyn/client/bddf_download.rst","python/bosdyn-client/src/bosdyn/client/channel.rst","python/bosdyn-client/src/bosdyn/client/command_line.rst","python/bosdyn-client/src/bosdyn/client/common.rst","python/bosdyn-client/src/bosdyn/client/data_acquisition.rst","python/bosdyn-client/src/bosdyn/client/data_acquisition_helpers.rst","python/bosdyn-client/src/bosdyn/client/data_acquisition_plugin.rst","python/bosdyn-client/src/bosdyn/client/data_acquisition_plugin_service.rst","python/bosdyn-client/src/bosdyn/client/data_acquisition_store.rst","python/bosdyn-client/src/bosdyn/client/data_buffer.rst","python/bosdyn-client/src/bosdyn/client/data_service.rst","python/bosdyn-client/src/bosdyn/client/directory.rst","python/bosdyn-client/src/bosdyn/client/directory_registration.rst","python/bosdyn-client/src/bosdyn/client/docking.rst","python/bosdyn-client/src/bosdyn/client/door.rst","python/bosdyn-client/src/bosdyn/client/estop.rst","python/bosdyn-client/src/bosdyn/client/exceptions.rst","python/bosdyn-client/src/bosdyn/client/fault.rst","python/bosdyn-client/src/bosdyn/client/frame_helpers.rst","python/bosdyn-client/src/bosdyn/client/graph_nav.rst","python/bosdyn-client/src/bosdyn/client/image.rst","python/bosdyn-client/src/bosdyn/client/image_service_helpers.rst","python/bosdyn-client/src/bosdyn/client/lease.rst","python/bosdyn-client/src/bosdyn/client/license.rst","python/bosdyn-client/src/bosdyn/client/local_grid.rst","python/bosdyn-client/src/bosdyn/client/log_annotation.rst","python/bosdyn-client/src/bosdyn/client/manipulation_api_client.rst","python/bosdyn-client/src/bosdyn/client/math_helpers.rst","python/bosdyn-client/src/bosdyn/client/network_compute_bridge_client.rst","python/bosdyn-client/src/bosdyn/client/payload.rst","python/bosdyn-client/src/bosdyn/client/payload_registration.rst","python/bosdyn-client/src/bosdyn/client/point_cloud.rst","python/bosdyn-client/src/bosdyn/client/power.rst","python/bosdyn-client/src/bosdyn/client/processors.rst","python/bosdyn-client/src/bosdyn/client/recording.rst","python/bosdyn-client/src/bosdyn/client/robot.rst","python/bosdyn-client/src/bosdyn/client/robot_command.rst","python/bosdyn-client/src/bosdyn/client/robot_id.rst","python/bosdyn-client/src/bosdyn/client/robot_state.rst","python/bosdyn-client/src/bosdyn/client/sdk.rst","python/bosdyn-client/src/bosdyn/client/server_util.rst","python/bosdyn-client/src/bosdyn/client/spot_cam/README.md","python/bosdyn-client/src/bosdyn/client/spot_cam/audio.rst","python/bosdyn-client/src/bosdyn/client/spot_cam/compositor.rst","python/bosdyn-client/src/bosdyn/client/spot_cam/health.rst","python/bosdyn-client/src/bosdyn/client/spot_cam/lighting.rst","python/bosdyn-client/src/bosdyn/client/spot_cam/media_log.rst","python/bosdyn-client/src/bosdyn/client/spot_cam/network.rst","python/bosdyn-client/src/bosdyn/client/spot_cam/power.rst","python/bosdyn-client/src/bosdyn/client/spot_cam/ptz.rst","python/bosdyn-client/src/bosdyn/client/spot_cam/streamquality.rst","python/bosdyn-client/src/bosdyn/client/spot_cam/version.rst","python/bosdyn-client/src/bosdyn/client/spot_check.rst","python/bosdyn-client/src/bosdyn/client/time_sync.rst","python/bosdyn-client/src/bosdyn/client/token_cache.rst","python/bosdyn-client/src/bosdyn/client/token_manager.rst","python/bosdyn-client/src/bosdyn/client/util.rst","python/bosdyn-client/src/bosdyn/client/world_object.rst","python/bosdyn-core/src/bosdyn/README.md","python/bosdyn-core/src/bosdyn/bddf/README.md","python/bosdyn-core/src/bosdyn/bddf/base_data_reader.rst","python/bosdyn-core/src/bosdyn/bddf/block_writer.rst","python/bosdyn-core/src/bosdyn/bddf/bosdyn.rst","python/bosdyn-core/src/bosdyn/bddf/common.rst","python/bosdyn-core/src/bosdyn/bddf/data_reader.rst","python/bosdyn-core/src/bosdyn/bddf/data_writer.rst","python/bosdyn-core/src/bosdyn/bddf/file_indexer.rst","python/bosdyn-core/src/bosdyn/bddf/grpc_proto_reader.rst","python/bosdyn-core/src/bosdyn/bddf/grpc_reader.rst","python/bosdyn-core/src/bosdyn/bddf/grpc_service_reader.rst","python/bosdyn-core/src/bosdyn/bddf/grpc_service_writer.rst","python/bosdyn-core/src/bosdyn/bddf/pod_series_reader.rst","python/bosdyn-core/src/bosdyn/bddf/pod_series_writer.rst","python/bosdyn-core/src/bosdyn/bddf/protobuf_channel_reader.rst","python/bosdyn-core/src/bosdyn/bddf/protobuf_reader.rst","python/bosdyn-core/src/bosdyn/bddf/protobuf_series_writer.rst","python/bosdyn-core/src/bosdyn/bddf/stream_data_reader.rst","python/bosdyn-core/src/bosdyn/geometry.rst","python/bosdyn-core/src/bosdyn/util.rst","python/bosdyn-mission/src/bosdyn/mission/README.md","python/bosdyn-mission/src/bosdyn/mission/client.rst","python/bosdyn-mission/src/bosdyn/mission/constants.rst","python/bosdyn-mission/src/bosdyn/mission/exceptions.rst","python/bosdyn-mission/src/bosdyn/mission/remote_client.rst","python/bosdyn-mission/src/bosdyn/mission/server_util.rst","python/bosdyn-mission/src/bosdyn/mission/util.rst","python/examples/README.md","python/examples/arm_and_mobility_command/README.md","python/examples/arm_door/README.md","python/examples/arm_force_control/README.md","python/examples/arm_gaze/README.md","python/examples/arm_gcode/README.md","python/examples/arm_grasp/README.md","python/examples/arm_joint_move/README.md","python/examples/arm_simple/README.md","python/examples/arm_stow_unstow/README.md","python/examples/arm_surface_contact/README.md","python/examples/arm_trajectory/README.md","python/examples/arm_walk_to_object/README.md","python/examples/arm_with_body_follow/README.md","python/examples/bddf_download/README.md","python/examples/cloud_upload/README.md","python/examples/comms_test/README.md","python/examples/data_acquisition_service/README.md","python/examples/data_service/README.md","python/examples/directory/README.md","python/examples/docking/README.md","python/examples/docs/arm_examples.md","python/examples/docs/autonomy_and_missions_examples.md","python/examples/docs/basic_service_examples.md","python/examples/docs/data_acquisition_examples.md","python/examples/docs/logging_examples.md","python/examples/docs/payloads_examples.md","python/examples/docs/perception_world_objects_examples.md","python/examples/docs/robot_behavior_examples.md","python/examples/estop/README.md","python/examples/fiducial_follow/README.md","python/examples/frame_trajectory_command/README.md","python/examples/get_image/README.md","python/examples/get_mission_state/README.md","python/examples/get_robot_state/README.md","python/examples/get_robot_state_async/README.md","python/examples/get_world_objects/README.md","python/examples/graph_nav_command_line/README.md","python/examples/graph_nav_view_map/README.md","python/examples/hello_spot/README.md","python/examples/logging/README.md","python/examples/mission_question_answerer/README.md","python/examples/mission_recorder/README.md","python/examples/network_compute_bridge/README.md","python/examples/payloads/README.md","python/examples/remote_mission_service/README.md","python/examples/replay_mission/README.md","python/examples/ricoh_theta/README.md","python/examples/self_registration/README.md","python/examples/service_faults/README.md","python/examples/spot_cam/README.md","python/examples/spot_detect_and_follow/README.md","python/examples/spot_light/README.md","python/examples/spot_tensorflow_detector/README.md","python/examples/stance/README.md","python/examples/stitch_front_images/README.md","python/examples/tester_programs/README.md","python/examples/time_sync/README.md","python/examples/upload_choreographed_sequence/README.md","python/examples/velodyne_client/README.md","python/examples/visualizer/README.md","python/examples/wasd/README.md","python/examples/web_cam_image_service/README.md","python/examples/world_object_mutations/README.md","python/examples/world_object_with_image_coordinates/README.md","python/examples/xbox_controller/README.md"],objects:{"bosdyn.bddf":{base_data_reader:[133,0,0,"-"],block_writer:[134,0,0,"-"],bosdyn:[135,0,0,"-"],common:[136,0,0,"-"],data_reader:[137,0,0,"-"],data_writer:[138,0,0,"-"],file_indexer:[139,0,0,"-"],grpc_proto_reader:[140,0,0,"-"],grpc_reader:[141,0,0,"-"],grpc_service_reader:[142,0,0,"-"],grpc_service_writer:[143,0,0,"-"],pod_series_reader:[144,0,0,"-"],pod_series_writer:[145,0,0,"-"],protobuf_channel_reader:[146,0,0,"-"],protobuf_reader:[147,0,0,"-"],protobuf_series_writer:[148,0,0,"-"],stream_data_reader:[149,0,0,"-"]},"bosdyn.bddf.base_data_reader":{BaseDataReader:[133,1,1,""]},"bosdyn.bddf.base_data_reader.BaseDataReader":{annotations:[133,2,1,""],checksum:[133,2,1,""],file_descriptor:[133,2,1,""],file_index:[133,2,1,""],read_checksum:[133,2,1,""],series_spec_to_index:[133,2,1,""],version:[133,2,1,""]},"bosdyn.bddf.block_writer":{BlockWriter:[134,1,1,""]},"bosdyn.bddf.block_writer.BlockWriter":{close:[134,2,1,""],closed:[134,2,1,""],tell:[134,2,1,""],write_data_block:[134,2,1,""],write_descriptor_block:[134,2,1,""],write_file_end:[134,2,1,""],write_header:[134,2,1,""]},"bosdyn.bddf.bosdyn":{GrpcRequests:[135,1,1,""],GrpcResponses:[135,1,1,""],MessageChannel:[135,1,1,""],TypedMessageChannel:[135,1,1,""]},"bosdyn.bddf.bosdyn.GrpcRequests":{KEYS:[135,3,1,""],MESSAGE_TYPE:[135,3,1,""],SERIES_TYPE:[135,3,1,""],SERVICE_NAME:[135,3,1,""]},"bosdyn.bddf.bosdyn.GrpcResponses":{KEYS:[135,3,1,""],MESSAGE_TYPE:[135,3,1,""],SERIES_TYPE:[135,3,1,""],SERVICE_NAME:[135,3,1,""]},"bosdyn.bddf.bosdyn.MessageChannel":{CHANNEL:[135,3,1,""],KEYS:[135,3,1,""],SERIES_TYPE:[135,3,1,""]},"bosdyn.bddf.bosdyn.TypedMessageChannel":{CHANNEL:[135,3,1,""],KEYS:[135,3,1,""],MESSAGE_TYPE:[135,3,1,""],SERIES_TYPE:[135,3,1,""]},"bosdyn.bddf.common":{AddSeriesError:[136,4,1,""],ChecksumError:[136,4,1,""],DataError:[136,4,1,""],DataFormatError:[136,4,1,""],ParseError:[136,4,1,""],SeriesIdentifier:[136,1,1,""],SeriesNotUniqueError:[136,4,1,""]},"bosdyn.bddf.common.SeriesIdentifier":{KEYS:[136,3,1,""],SERIES_TYPE:[136,3,1,""]},"bosdyn.bddf.data_reader":{DataReader:[137,1,1,""]},"bosdyn.bddf.data_reader.DataReader":{num_data_blocks:[137,2,1,""],read:[137,2,1,""],series_block_index:[137,2,1,""],series_descriptor:[137,2,1,""],total_bytes:[137,2,1,""]},"bosdyn.bddf.data_writer":{DataWriter:[138,1,1,""]},"bosdyn.bddf.data_writer.DataWriter":{add_message_series:[138,2,1,""],add_pod_series:[138,2,1,""],add_series:[138,2,1,""],file_index:[138,2,1,""],run_on_close:[138,2,1,""],write_data:[138,2,1,""]},"bosdyn.bddf.file_indexer":{FileIndexer:[139,1,1,""]},"bosdyn.bddf.file_indexer.FileIndexer":{add_series:[139,2,1,""],add_series_descriptor:[139,2,1,""],descriptor_index:[139,2,1,""],file_index:[139,2,1,""],index_data_block:[139,2,1,""],make_data_descriptor:[139,2,1,""],series_block_indexes:[139,2,1,""],series_descriptor:[139,2,1,""],series_identifier_to_hash:[139,2,1,""],write_index:[139,2,1,""]},"bosdyn.bddf.grpc_proto_reader":{GrpcProtoReader:[140,1,1,""]},"bosdyn.bddf.grpc_proto_reader.GrpcProtoReader":{get_message:[140,2,1,""],num_messages:[140,2,1,""]},"bosdyn.bddf.grpc_reader":{GrpcReader:[141,1,1,""]},"bosdyn.bddf.grpc_reader.GrpcReader":{data_reader:[141,2,1,""],get_message:[141,2,1,""],get_proto_reader:[141,2,1,""]},"bosdyn.bddf.grpc_service_reader":{GrpcServiceReader:[142,1,1,""]},"bosdyn.bddf.grpc_service_reader.GrpcServiceReader":{add_proto_reader:[142,2,1,""],data_reader:[142,2,1,""],get_proto_reader:[142,2,1,""]},"bosdyn.bddf.grpc_service_writer":{GrpcServiceWriter:[143,1,1,""]},"bosdyn.bddf.grpc_service_writer.GrpcServiceWriter":{log_request:[143,2,1,""],log_response:[143,2,1,""]},"bosdyn.bddf.pod_series_reader":{PodSeriesReader:[144,1,1,""]},"bosdyn.bddf.pod_series_reader.PodSeriesReader":{num_data_blocks:[144,2,1,""],pod_type:[144,2,1,""],read_samples:[144,2,1,""],series_descriptor:[144,2,1,""]},"bosdyn.bddf.pod_series_writer":{PodSeriesWriter:[145,1,1,""]},"bosdyn.bddf.pod_series_writer.PodSeriesWriter":{finish_block:[145,2,1,""],series_spec:[145,2,1,""],series_type:[145,2,1,""],write:[145,2,1,""]},"bosdyn.bddf.protobuf_channel_reader":{ProtobufChannelReader:[146,1,1,""]},"bosdyn.bddf.protobuf_channel_reader.ProtobufChannelReader":{get_message:[146,2,1,""],num_messages:[146,2,1,""],series_descriptor:[146,2,1,""]},"bosdyn.bddf.protobuf_reader":{ProtobufReader:[147,1,1,""]},"bosdyn.bddf.protobuf_reader.ProtobufReader":{data_reader:[147,2,1,""],get_message:[147,2,1,""],series_index:[147,2,1,""],series_index_to_descriptor:[147,2,1,""]},"bosdyn.bddf.protobuf_series_writer":{ProtobufSeriesWriter:[148,1,1,""]},"bosdyn.bddf.protobuf_series_writer.ProtobufSeriesWriter":{series_spec:[148,2,1,""],series_type:[148,2,1,""],write:[148,2,1,""]},"bosdyn.bddf.stream_data_reader":{StreamDataReader:[149,1,1,""]},"bosdyn.bddf.stream_data_reader.StreamDataReader":{eof:[149,2,1,""],read_checksum:[149,2,1,""],read_data_block:[149,2,1,""],read_next_block:[149,2,1,""],series_block_index:[149,2,1,""],series_block_indexes:[149,2,1,""],series_descriptor:[149,2,1,""],stream_file_index:[149,2,1,""]},"bosdyn.choreography.client":{choreography:[67,0,0,"-"]},"bosdyn.choreography.client.choreography":{ChoreographyClient:[67,1,1,""],InvalidUploadedChoreographyError:[67,4,1,""],LeaseError:[67,4,1,""],RobotCommandIssuesError:[67,4,1,""],load_choreography_sequence_from_binary_file:[67,5,1,""],load_choreography_sequence_from_txt_file:[67,5,1,""],save_choreography_sequence_to_file:[67,5,1,""]},"bosdyn.choreography.client.choreography.ChoreographyClient":{build_execute_choreography_request:[67,2,1,""],default_service_name:[67,3,1,""],execute_choreography:[67,2,1,""],execute_choreography_async:[67,2,1,""],list_all_moves:[67,2,1,""],list_all_moves_async:[67,2,1,""],service_type:[67,3,1,""],timesync_endpoint:[67,2,1,""],update_from:[67,2,1,""],upload_choreography:[67,2,1,""],upload_choreography_async:[67,2,1,""]},"bosdyn.client":{arm_surface_contact:[69,0,0,"-"],async_tasks:[70,0,0,"-"],auth:[71,0,0,"-"],bddf:[72,0,0,"-"],bddf_download:[73,0,0,"-"],channel:[74,0,0,"-"],command_line:[75,0,0,"-"],common:[76,0,0,"-"],data_acquisition:[77,0,0,"-"],data_acquisition_helpers:[78,0,0,"-"],data_acquisition_plugin:[79,0,0,"-"],data_acquisition_plugin_service:[80,0,0,"-"],data_acquisition_store:[81,0,0,"-"],data_buffer:[82,0,0,"-"],data_service:[83,0,0,"-"],directory:[84,0,0,"-"],directory_registration:[85,0,0,"-"],docking:[86,0,0,"-"],door:[87,0,0,"-"],estop:[88,0,0,"-"],exceptions:[89,0,0,"-"],fault:[90,0,0,"-"],frame_helpers:[91,0,0,"-"],graph_nav:[92,0,0,"-"],image:[93,0,0,"-"],image_service_helpers:[94,0,0,"-"],lease:[95,0,0,"-"],license:[96,0,0,"-"],local_grid:[97,0,0,"-"],log_annotation:[98,0,0,"-"],manipulation_api_client:[99,0,0,"-"],math_helpers:[100,0,0,"-"],network_compute_bridge_client:[101,0,0,"-"],payload:[102,0,0,"-"],payload_registration:[103,0,0,"-"],point_cloud:[104,0,0,"-"],power:[105,0,0,"-"],processors:[106,0,0,"-"],recording:[107,0,0,"-"],robot:[108,0,0,"-"],robot_command:[109,0,0,"-"],robot_id:[110,0,0,"-"],robot_state:[111,0,0,"-"],sdk:[112,0,0,"-"],server_util:[113,0,0,"-"],spot_check:[125,0,0,"-"],time_sync:[126,0,0,"-"],token_cache:[127,0,0,"-"],token_manager:[128,0,0,"-"],util:[129,0,0,"-"],world_object:[130,0,0,"-"]},"bosdyn.client.arm_surface_contact":{ArmSurfaceContactClient:[69,1,1,""]},"bosdyn.client.arm_surface_contact.ArmSurfaceContactClient":{arm_surface_contact_command:[69,2,1,""],arm_surface_contact_command_async:[69,2,1,""],default_service_name:[69,3,1,""],service_type:[69,3,1,""],update_from:[69,2,1,""]},"bosdyn.client.async_tasks":{AsyncGRPCTask:[70,1,1,""],AsyncPeriodicGRPCTask:[70,1,1,""],AsyncPeriodicQuery:[70,1,1,""],AsyncTasks:[70,1,1,""]},"bosdyn.client.async_tasks.AsyncGRPCTask":{update:[70,2,1,""]},"bosdyn.client.async_tasks.AsyncPeriodicQuery":{proto:[70,2,1,""]},"bosdyn.client.async_tasks.AsyncTasks":{add_task:[70,2,1,""],update:[70,2,1,""]},"bosdyn.client.auth":{AuthClient:[71,1,1,""],AuthResponseError:[71,4,1,""],ExpiredApplicationTokenError:[71,4,1,""],InvalidApplicationTokenError:[71,4,1,""],InvalidLoginError:[71,4,1,""],InvalidTokenError:[71,4,1,""],TemporarilyLockedOutError:[71,4,1,""]},"bosdyn.client.auth.AuthClient":{auth:[71,2,1,""],auth_async:[71,2,1,""],auth_with_token:[71,2,1,""],auth_with_token_async:[71,2,1,""],default_service_name:[71,3,1,""],service_type:[71,3,1,""]},"bosdyn.client.bddf_download":{download_data:[73,5,1,""],main:[73,5,1,""]},"bosdyn.client.channel":{RefreshingAccessTokenAuthMetadataPlugin:[74,1,1,""],create_insecure_channel:[74,5,1,""],create_secure_channel:[74,5,1,""],create_secure_channel_creds:[74,5,1,""],generate_channel_options:[74,5,1,""],translate_exception:[74,5,1,""]},"bosdyn.client.command_line":{BecomeEstopCommand:[75,1,1,""],Command:[75,1,1,""],DataAcquisitionCommand:[75,1,1,""],DataAcquisitionRequestCommand:[75,1,1,""],DataAcquisitionServiceCommand:[75,1,1,""],DataAcquisitionStatusCommand:[75,1,1,""],DataBufferCommands:[75,1,1,""],DataServiceCommands:[75,1,1,""],DirectoryCommands:[75,1,1,""],DirectoryGetCommand:[75,1,1,""],DirectoryListCommand:[75,1,1,""],DirectoryRegisterCommand:[75,1,1,""],DirectoryUnregisterCommand:[75,1,1,""],FaultCommands:[75,1,1,""],FaultShowCommand:[75,1,1,""],FaultWatchCommand:[75,1,1,""],FullStateCommand:[75,1,1,""],GetDataBufferCommentsCommand:[75,1,1,""],GetDataBufferEventsCommand:[75,1,1,""],GetDataBufferEventsCommentsCommand:[75,1,1,""],GetDataBufferStatusCommand:[75,1,1,""],GetImageCommand:[75,1,1,""],GetLocalGridsCommand:[75,1,1,""],HardwareConfigurationCommand:[75,1,1,""],HostComputerIPCommand:[75,1,1,""],ImageCommands:[75,1,1,""],LeaseCommands:[75,1,1,""],LeaseListCommand:[75,1,1,""],LicenseCommand:[75,1,1,""],ListImageSourcesCommand:[75,1,1,""],ListLocalGridTypesCommand:[75,1,1,""],LocalGridCommands:[75,1,1,""],MetricsCommand:[75,1,1,""],OperatorCommentCommand:[75,1,1,""],PayloadCommands:[75,1,1,""],PayloadListCommand:[75,1,1,""],PayloadRegisterCommand:[75,1,1,""],PowerCommand:[75,1,1,""],PowerPayloadsCommand:[75,1,1,""],PowerRobotCommand:[75,1,1,""],PowerWifiRadioCommand:[75,1,1,""],RobotIdCommand:[75,1,1,""],RobotModel:[75,1,1,""],RobotStateCommands:[75,1,1,""],Subcommands:[75,1,1,""],TextMsgCommand:[75,1,1,""],TimeSyncCommand:[75,1,1,""],main:[75,5,1,""]},"bosdyn.client.command_line.BecomeEstopCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.Command":{NAME:[75,3,1,""],NEED_AUTHENTICATION:[75,3,1,""],run:[75,2,1,""]},"bosdyn.client.command_line.DataAcquisitionCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.DataAcquisitionRequestCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.DataAcquisitionServiceCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.DataAcquisitionStatusCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.DataBufferCommands":{NAME:[75,3,1,""]},"bosdyn.client.command_line.DataServiceCommands":{NAME:[75,3,1,""]},"bosdyn.client.command_line.DirectoryCommands":{NAME:[75,3,1,""]},"bosdyn.client.command_line.DirectoryGetCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.DirectoryListCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.DirectoryRegisterCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.DirectoryUnregisterCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.FaultCommands":{NAME:[75,3,1,""]},"bosdyn.client.command_line.FaultShowCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.FaultWatchCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.FullStateCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.GetDataBufferCommentsCommand":{NAME:[75,3,1,""],pretty_print:[75,2,1,""]},"bosdyn.client.command_line.GetDataBufferEventsCommand":{NAME:[75,3,1,""],pretty_print:[75,2,1,""]},"bosdyn.client.command_line.GetDataBufferEventsCommentsCommand":{pretty_print:[75,2,1,""]},"bosdyn.client.command_line.GetDataBufferStatusCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.GetImageCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.GetLocalGridsCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.HardwareConfigurationCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.HostComputerIPCommand":{NAME:[75,3,1,""],NEED_AUTHENTICATION:[75,3,1,""]},"bosdyn.client.command_line.ImageCommands":{NAME:[75,3,1,""]},"bosdyn.client.command_line.LeaseCommands":{NAME:[75,3,1,""]},"bosdyn.client.command_line.LeaseListCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.LicenseCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.ListImageSourcesCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.ListLocalGridTypesCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.LocalGridCommands":{NAME:[75,3,1,""]},"bosdyn.client.command_line.MetricsCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.OperatorCommentCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.PayloadCommands":{NAME:[75,3,1,""]},"bosdyn.client.command_line.PayloadListCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.PayloadRegisterCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.PowerCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.PowerPayloadsCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.PowerRobotCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.PowerWifiRadioCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.RobotIdCommand":{NAME:[75,3,1,""],NEED_AUTHENTICATION:[75,3,1,""]},"bosdyn.client.command_line.RobotModel":{NAME:[75,3,1,""],NEED_AUTHENTICATION:[75,3,1,""]},"bosdyn.client.command_line.RobotStateCommands":{NAME:[75,3,1,""]},"bosdyn.client.command_line.TextMsgCommand":{NAME:[75,3,1,""]},"bosdyn.client.command_line.TimeSyncCommand":{NAME:[75,3,1,""]},"bosdyn.client.common":{BaseClient:[76,1,1,""],FutureWrapper:[76,1,1,""],common_header_errors:[76,5,1,""],common_lease_errors:[76,5,1,""],error_factory:[76,5,1,""],error_pair:[76,5,1,""],get_self_ip:[76,5,1,""],handle_common_header_errors:[76,5,1,""],handle_lease_use_result_errors:[76,5,1,""],handle_unset_status_error:[76,5,1,""],print_response:[76,5,1,""],process_kwargs:[76,5,1,""],streaming_common_header_errors:[76,5,1,""],streaming_common_lease_errors:[76,5,1,""]},"bosdyn.client.common.BaseClient":{call:[76,2,1,""],call_async:[76,2,1,""],channel:[76,2,1,""],handle_response:[76,2,1,""],handle_response_streaming:[76,2,1,""],request_trim_for_log:[76,2,1,""],response_trim_for_log:[76,2,1,""],update_from:[76,2,1,""],update_request_iterator:[76,2,1,""],update_response_iterator:[76,2,1,""]},"bosdyn.client.common.FutureWrapper":{add_done_callback:[76,2,1,""],cancel:[76,2,1,""],cancelled:[76,2,1,""],done:[76,2,1,""],exception:[76,2,1,""],result:[76,2,1,""],running:[76,2,1,""],traceback:[76,2,1,""]},"bosdyn.client.data_acquisition":{CancellationFailedError:[77,4,1,""],DataAcquisitionClient:[77,1,1,""],DataAcquisitionResponseError:[77,4,1,""],RequestIdDoesNotExistError:[77,4,1,""],UnknownCaptureTypeError:[77,4,1,""],acquire_data_error:[77,5,1,""],get_request_id:[77,5,1,""],metadata_to_proto:[77,5,1,""]},"bosdyn.client.data_acquisition.DataAcquisitionClient":{acquire_data:[77,2,1,""],acquire_data_async:[77,2,1,""],cancel_acquisition:[77,2,1,""],cancel_acquisition_async:[77,2,1,""],default_service_name:[77,3,1,""],get_service_info:[77,2,1,""],get_service_info_async:[77,2,1,""],get_status:[77,2,1,""],get_status_async:[77,2,1,""],service_type:[77,3,1,""],update_from:[77,2,1,""]},"bosdyn.client.data_acquisition_helpers":{acquire_and_process_request:[78,5,1,""],cancel_acquisition_request:[78,5,1,""],clean_filename:[78,5,1,""],download_data_REST:[78,5,1,""],issue_acquire_data_request:[78,5,1,""],make_time_query_params:[78,5,1,""],make_time_query_params_from_group_name:[78,5,1,""]},"bosdyn.client.data_acquisition_plugin":{DataAcquisitionPluginClient:[79,1,1,""]},"bosdyn.client.data_acquisition_plugin.DataAcquisitionPluginClient":{acquire_plugin_data:[79,2,1,""],acquire_plugin_data_async:[79,2,1,""],cancel_acquisition:[79,2,1,""],cancel_acquisition_async:[79,2,1,""],default_service_name:[79,3,1,""],get_service_info:[79,2,1,""],get_service_info_async:[79,2,1,""],get_status:[79,2,1,""],get_status_async:[79,2,1,""],service_type:[79,3,1,""],update_from:[79,2,1,""]},"bosdyn.client.data_acquisition_plugin_service":{DataAcquisitionPluginService:[80,1,1,""],DataAcquisitionStoreHelper:[80,1,1,""],RequestCancelledError:[80,4,1,""],RequestManager:[80,1,1,""],RequestState:[80,1,1,""],make_error:[80,5,1,""]},"bosdyn.client.data_acquisition_plugin_service.DataAcquisitionPluginService":{AcquirePluginData:[80,2,1,""],CancelAcquisition:[80,2,1,""],GetServiceInfo:[80,2,1,""],GetStatus:[80,2,1,""],acquire_response_fn:[80,3,1,""],capabilities:[80,3,1,""],data_collect_fn:[80,3,1,""],executor:[80,3,1,""],logger:[80,3,1,""],request_manager:[80,3,1,""],robot:[80,3,1,""],service_type:[80,3,1,""],store_client:[80,3,1,""]},"bosdyn.client.data_acquisition_plugin_service.DataAcquisitionStoreHelper":{cancel_check:[80,2,1,""],cancel_interval:[80,3,1,""],data_id_future_pairs:[80,3,1,""],state:[80,3,1,""],store_client:[80,3,1,""],store_data:[80,2,1,""],store_image:[80,2,1,""],store_metadata:[80,2,1,""],wait_for_stores_complete:[80,2,1,""]},"bosdyn.client.data_acquisition_plugin_service.RequestManager":{add_request:[80,2,1,""],cleanup_requests:[80,2,1,""],get_request_state:[80,2,1,""],get_status_proto:[80,2,1,""],mark_request_cancelled:[80,2,1,""],mark_request_finished:[80,2,1,""]},"bosdyn.client.data_acquisition_plugin_service.RequestState":{add_errors:[80,2,1,""],add_saved:[80,2,1,""],cancel_check:[80,2,1,""],has_data_errors:[80,2,1,""],is_cancelled:[80,2,1,""],kNonError:[80,3,1,""],set_complete_if_no_error:[80,2,1,""],set_status:[80,2,1,""]},"bosdyn.client.data_acquisition_store":{DataAcquisitionStoreClient:[81,1,1,""]},"bosdyn.client.data_acquisition_store.DataAcquisitionStoreClient":{default_service_name:[81,3,1,""],list_capture_actions:[81,2,1,""],list_capture_actions_async:[81,2,1,""],list_stored_data:[81,2,1,""],list_stored_data_async:[81,2,1,""],list_stored_images:[81,2,1,""],list_stored_images_async:[81,2,1,""],list_stored_metadata:[81,2,1,""],list_stored_metadata_async:[81,2,1,""],service_type:[81,3,1,""],store_data:[81,2,1,""],store_data_async:[81,2,1,""],store_image:[81,2,1,""],store_image_async:[81,2,1,""],store_metadata:[81,2,1,""],store_metadata_async:[81,2,1,""],update_from:[81,2,1,""]},"bosdyn.client.data_buffer":{DataBufferClient:[82,1,1,""],InvalidArgument:[82,4,1,""]},"bosdyn.client.data_buffer.DataBufferClient":{add_blob:[82,2,1,""],add_blob_async:[82,2,1,""],add_events:[82,2,1,""],add_events_async:[82,2,1,""],add_operator_comment:[82,2,1,""],add_operator_comment_async:[82,2,1,""],add_protobuf:[82,2,1,""],add_protobuf_async:[82,2,1,""],add_signal_tick:[82,2,1,""],add_signal_tick_async:[82,2,1,""],add_text_messages:[82,2,1,""],add_text_messages_async:[82,2,1,""],default_service_name:[82,3,1,""],register_signal_schema:[82,2,1,""],register_signal_schema_async:[82,2,1,""],service_type:[82,3,1,""],update_from:[82,2,1,""]},"bosdyn.client.data_service":{DataServiceClient:[83,1,1,""],InvalidArgument:[83,4,1,""]},"bosdyn.client.data_service.DataServiceClient":{default_service_name:[83,3,1,""],delete_data_pages:[83,2,1,""],delete_data_pages_async:[83,2,1,""],get_data_buffer_status:[83,2,1,""],get_data_buffer_status_async:[83,2,1,""],get_data_index:[83,2,1,""],get_data_index_async:[83,2,1,""],get_data_pages:[83,2,1,""],get_data_pages_async:[83,2,1,""],get_events_comments:[83,2,1,""],get_events_comments_async:[83,2,1,""],service_type:[83,3,1,""],update_from:[83,2,1,""]},"bosdyn.client.directory":{DirectoryClient:[84,1,1,""],DirectoryResponseError:[84,4,1,""],NonexistentServiceError:[84,4,1,""]},"bosdyn.client.directory.DirectoryClient":{default_service_name:[84,3,1,""],get_entry:[84,2,1,""],get_entry_async:[84,2,1,""],list:[84,2,1,""],list_async:[84,2,1,""],service_type:[84,3,1,""]},"bosdyn.client.directory_registration":{DirectoryRegistrationClient:[85,1,1,""],DirectoryRegistrationKeepAlive:[85,1,1,""],DirectoryRegistrationResponseError:[85,4,1,""],ServiceAlreadyExistsError:[85,4,1,""],ServiceDoesNotExistError:[85,4,1,""],reset_service_registration:[85,5,1,""]},"bosdyn.client.directory_registration.DirectoryRegistrationClient":{default_service_name:[85,3,1,""],register:[85,2,1,""],service_type:[85,3,1,""],unregister:[85,2,1,""],update:[85,2,1,""]},"bosdyn.client.directory_registration.DirectoryRegistrationKeepAlive":{is_alive:[85,2,1,""],shutdown:[85,2,1,""],start:[85,2,1,""],unregister:[85,2,1,""]},"bosdyn.client.docking":{DockingClient:[86,1,1,""],blocking_dock_robot:[86,5,1,""],blocking_go_to_prep_pose:[86,5,1,""],blocking_undock:[86,5,1,""]},"bosdyn.client.docking.DockingClient":{default_service_name:[86,3,1,""],docking_command:[86,2,1,""],docking_command_async:[86,2,1,""],docking_command_feedback:[86,2,1,""],docking_command_feedback_async:[86,2,1,""],get_docking_config:[86,2,1,""],get_docking_config_async:[86,2,1,""],get_docking_state:[86,2,1,""],get_docking_state_async:[86,2,1,""],service_type:[86,3,1,""],update_from:[86,2,1,""]},"bosdyn.client.door":{DoorClient:[87,1,1,""]},"bosdyn.client.door.DoorClient":{default_service_name:[87,3,1,""],open_door:[87,2,1,""],open_door_async:[87,2,1,""],open_door_feedback:[87,2,1,""],open_door_feedback_async:[87,2,1,""],service_type:[87,3,1,""],update_from:[87,2,1,""]},"bosdyn.client.estop":{ConfigMismatchError:[88,4,1,""],EndpointMismatchError:[88,4,1,""],EndpointUnknownError:[88,4,1,""],EstopClient:[88,1,1,""],EstopEndpoint:[88,1,1,""],EstopKeepAlive:[88,1,1,""],EstopResponseError:[88,4,1,""],IncorrectChallengeResponseError:[88,4,1,""],InvalidEndpointError:[88,4,1,""],InvalidIdError:[88,4,1,""],MotorsOnError:[88,4,1,""],StopLevel:[88,1,1,""],is_estopped:[88,5,1,""],response_from_challenge:[88,5,1,""]},"bosdyn.client.estop.EstopClient":{check_in:[88,2,1,""],check_in_async:[88,2,1,""],default_service_name:[88,3,1,""],deregister:[88,2,1,""],deregister_async:[88,2,1,""],get_config:[88,2,1,""],get_config_async:[88,2,1,""],get_status:[88,2,1,""],get_status_async:[88,2,1,""],register:[88,2,1,""],register_async:[88,2,1,""],service_type:[88,3,1,""],set_config:[88,2,1,""],set_config_async:[88,2,1,""]},"bosdyn.client.estop.EstopEndpoint":{REQUIRED_ROLE:[88,3,1,""],allow:[88,2,1,""],allow_async:[88,2,1,""],check_in_at_level:[88,2,1,""],check_in_at_level_async:[88,2,1,""],deregister:[88,2,1,""],deregister_async:[88,2,1,""],force_simple_setup:[88,2,1,""],from_proto:[88,2,1,""],get_challenge:[88,2,1,""],register:[88,2,1,""],set_challenge:[88,2,1,""],settle_then_cut:[88,2,1,""],settle_then_cut_async:[88,2,1,""],stop:[88,2,1,""],stop_async:[88,2,1,""],to_proto:[88,2,1,""],unique_id:[88,2,1,""]},"bosdyn.client.estop.EstopKeepAlive":{KeepAliveStatus:[88,1,1,""],allow:[88,2,1,""],client:[88,2,1,""],endpoint:[88,2,1,""],logger:[88,2,1,""],settle_then_cut:[88,2,1,""],shutdown:[88,2,1,""],stop:[88,2,1,""]},"bosdyn.client.estop.EstopKeepAlive.KeepAliveStatus":{DISABLED:[88,3,1,""],ERROR:[88,3,1,""],OK:[88,3,1,""]},"bosdyn.client.estop.StopLevel":{ESTOP_LEVEL_CUT:[88,3,1,""],ESTOP_LEVEL_NONE:[88,3,1,""],ESTOP_LEVEL_SETTLE_THEN_CUT:[88,3,1,""],ESTOP_LEVEL_UNKNOWN:[88,3,1,""]},"bosdyn.client.exceptions":{ClientCancelledOperationError:[89,4,1,""],Error:[89,4,1,""],InternalServerError:[89,4,1,""],InvalidAppTokenError:[89,4,1,""],InvalidClientCertificateError:[89,4,1,""],InvalidRequestError:[89,4,1,""],LeaseUseError:[89,4,1,""],LicenseError:[89,4,1,""],NonexistentAuthorityError:[89,4,1,""],NotFoundError:[89,4,1,""],PermissionDeniedError:[89,4,1,""],ProxyConnectionError:[89,4,1,""],ResponseError:[89,4,1,""],ResponseTooLargeError:[89,4,1,""],RetryableUnavailableError:[89,4,1,""],RpcError:[89,4,1,""],ServerError:[89,4,1,""],ServiceFailedDuringExecutionError:[89,4,1,""],ServiceUnavailableError:[89,4,1,""],TimeSyncRequired:[89,4,1,""],TimedOutError:[89,4,1,""],TransientFailureError:[89,4,1,""],UnableToConnectToRobotError:[89,4,1,""],UnauthenticatedError:[89,4,1,""],UnimplementedError:[89,4,1,""],UnknownDnsNameError:[89,4,1,""],UnsetStatusError:[89,4,1,""]},"bosdyn.client.fault":{FaultClient:[90,1,1,""],FaultResponseError:[90,4,1,""],ServiceFaultAlreadyExistsError:[90,4,1,""],ServiceFaultDoesNotExistError:[90,4,1,""]},"bosdyn.client.fault.FaultClient":{clear_service_fault:[90,2,1,""],clear_service_fault_async:[90,2,1,""],default_service_name:[90,3,1,""],service_type:[90,3,1,""],trigger_service_fault:[90,2,1,""],trigger_service_fault_async:[90,2,1,""]},"bosdyn.client.frame_helpers":{ChildFrameInTree:[91,4,1,""],Error:[91,4,1,""],GenerateTreeError:[91,4,1,""],ValidateFrameTreeCycleError:[91,4,1,""],ValidateFrameTreeDisjointError:[91,4,1,""],ValidateFrameTreeError:[91,4,1,""],ValidateFrameTreeUnknownFrameError:[91,4,1,""],add_edge_to_tree:[91,5,1,""],express_se2_velocity_in_new_frame:[91,5,1,""],express_se3_velocity_in_new_frame:[91,5,1,""],get_a_tform_b:[91,5,1,""],get_frame_names:[91,5,1,""],get_odom_tform_body:[91,5,1,""],get_se2_a_tform_b:[91,5,1,""],get_vision_tform_body:[91,5,1,""],is_gravity_aligned_frame_name:[91,5,1,""],validate_frame_tree_snapshot:[91,5,1,""]},"bosdyn.client.graph_nav":{CommandExpiredError:[92,4,1,""],ConstraintFaultError:[92,4,1,""],FeatureDesertError:[92,4,1,""],GraphNavClient:[92,1,1,""],GraphNavServiceResponseError:[92,4,1,""],InvalidEdgeError:[92,4,1,""],IsRecordingError:[92,4,1,""],NoPathError:[92,4,1,""],NoTimeSyncError:[92,4,1,""],RequestAbortedError:[92,4,1,""],RequestFailedError:[92,4,1,""],RobotFaultedError:[92,4,1,""],RobotImpairedError:[92,4,1,""],RobotLostError:[92,4,1,""],RobotNotLocalizedToRouteError:[92,4,1,""],RobotStateError:[92,4,1,""],RobotStuckError:[92,4,1,""],RouteError:[92,4,1,""],RouteNavigationError:[92,4,1,""],RouteNotUpdatingError:[92,4,1,""],TimeError:[92,4,1,""],TooDistantError:[92,4,1,""],UnknownMapInformationError:[92,4,1,""],UnknownWaypointError:[92,4,1,""],UnkownRouteElementsError:[92,4,1,""],UnrecongizedCommandError:[92,4,1,""]},"bosdyn.client.graph_nav.GraphNavClient":{build_route:[92,2,1,""],clear_graph:[92,2,1,""],clear_graph_async:[92,2,1,""],default_service_name:[92,3,1,""],download_edge_snapshot:[92,2,1,""],download_graph:[92,2,1,""],download_graph_async:[92,2,1,""],download_waypoint_snapshot:[92,2,1,""],generate_route_params:[92,2,1,""],generate_travel_params:[92,2,1,""],get_localization_state:[92,2,1,""],get_localization_state_async:[92,2,1,""],navigate_route:[92,2,1,""],navigate_route_async:[92,2,1,""],navigate_to:[92,2,1,""],navigate_to_async:[92,2,1,""],navigation_feedback:[92,2,1,""],navigation_feedback_async:[92,2,1,""],service_type:[92,3,1,""],set_localization:[92,2,1,""],set_localization_async:[92,2,1,""],update_from:[92,2,1,""],upload_edge_snapshot:[92,2,1,""],upload_graph:[92,2,1,""],upload_graph_async:[92,2,1,""],upload_waypoint_snapshot:[92,2,1,""],write_graph_and_snapshots:[92,2,1,""]},"bosdyn.client.image":{ImageClient:[93,1,1,""],ImageDataError:[93,4,1,""],ImageResponseError:[93,4,1,""],SourceDataError:[93,4,1,""],UnknownImageSourceError:[93,4,1,""],UnsupportedImageFormatRequestedError:[93,4,1,""],build_image_request:[93,5,1,""],save_images_as_files:[93,5,1,""],write_image_data:[93,5,1,""],write_pgm_or_ppm:[93,5,1,""]},"bosdyn.client.image.ImageClient":{default_service_name:[93,3,1,""],get_image:[93,2,1,""],get_image_async:[93,2,1,""],get_image_from_sources:[93,2,1,""],get_image_from_sources_async:[93,2,1,""],list_image_sources:[93,2,1,""],list_image_sources_async:[93,2,1,""],service_type:[93,3,1,""]},"bosdyn.client.image_service_helpers":{CameraBaseImageServicer:[94,1,1,""],CameraInterface:[94,1,1,""],ImageCaptureThread:[94,1,1,""],VisualImageSource:[94,1,1,""]},"bosdyn.client.image_service_helpers.CameraBaseImageServicer":{GetImage:[94,2,1,""],ListImageSources:[94,2,1,""]},"bosdyn.client.image_service_helpers.CameraInterface":{blocking_capture:[94,2,1,""],image_decode:[94,2,1,""]},"bosdyn.client.image_service_helpers.ImageCaptureThread":{get_latest_captured_image:[94,2,1,""],set_last_captured_image:[94,2,1,""],start_capturing:[94,2,1,""],stop_capturing:[94,2,1,""]},"bosdyn.client.image_service_helpers.VisualImageSource":{clear_fault:[94,2,1,""],create_capture_thread:[94,2,1,""],get_image_and_timestamp:[94,2,1,""],image_decode_with_error_checking:[94,2,1,""],initialize_faults:[94,2,1,""],make_capture_parameters:[94,2,1,""],make_image_source:[94,2,1,""],set_logger:[94,2,1,""],stop_capturing:[94,2,1,""],trigger_fault:[94,2,1,""]},"bosdyn.client.lease":{DisplacedLeaseError:[95,4,1,""],Error:[95,4,1,""],InvalidLeaseError:[95,4,1,""],InvalidResourceError:[95,4,1,""],Lease:[95,1,1,""],LeaseClient:[95,1,1,""],LeaseKeepAlive:[95,1,1,""],LeaseNotOwnedByWallet:[95,4,1,""],LeaseResponseError:[95,4,1,""],LeaseState:[95,1,1,""],LeaseWallet:[95,1,1,""],LeaseWalletRequestProcessor:[95,1,1,""],LeaseWalletResponseProcessor:[95,1,1,""],NoSuchLease:[95,4,1,""],NotActiveLeaseError:[95,4,1,""],NotAuthoritativeServiceError:[95,4,1,""],ResourceAlreadyClaimedError:[95,4,1,""],RevokedLeaseError:[95,4,1,""],UnmanagedResourceError:[95,4,1,""],WrongEpochError:[95,4,1,""],add_lease_wallet_processors:[95,5,1,""]},"bosdyn.client.lease.Lease":{CompareResult:[95,1,1,""],compare:[95,2,1,""],create_newer:[95,2,1,""],create_sublease:[95,2,1,""],is_valid_proto:[95,2,1,""]},"bosdyn.client.lease.Lease.CompareResult":{DIFFERENT_EPOCHS:[95,3,1,""],DIFFERENT_RESOURCES:[95,3,1,""],NEWER:[95,3,1,""],OLDER:[95,3,1,""],SAME:[95,3,1,""],SUB_LEASE:[95,3,1,""],SUPER_LEASE:[95,3,1,""]},"bosdyn.client.lease.LeaseClient":{acquire:[95,2,1,""],acquire_async:[95,2,1,""],default_service_name:[95,3,1,""],list_leases:[95,2,1,""],list_leases_async:[95,2,1,""],retain_lease:[95,2,1,""],retain_lease_async:[95,2,1,""],return_lease:[95,2,1,""],return_lease_async:[95,2,1,""],service_type:[95,3,1,""],take:[95,2,1,""],take_async:[95,2,1,""]},"bosdyn.client.lease.LeaseKeepAlive":{is_alive:[95,2,1,""],lease_wallet:[95,2,1,""],shutdown:[95,2,1,""],wait_until_done:[95,2,1,""]},"bosdyn.client.lease.LeaseState":{STATUS_NOT_MANAGED:[95,3,1,""],STATUS_OTHER_OWNER:[95,3,1,""],STATUS_REVOKED:[95,3,1,""],STATUS_SELF_OWNER:[95,3,1,""],STATUS_UNOWNED:[95,3,1,""],Status:[95,1,1,""],create_newer:[95,2,1,""],update_from_lease_use_result:[95,2,1,""]},"bosdyn.client.lease.LeaseState.Status":{NOT_MANAGED:[95,3,1,""],OTHER_OWNER:[95,3,1,""],REVOKED:[95,3,1,""],SELF_OWNER:[95,3,1,""],UNOWNED:[95,3,1,""]},"bosdyn.client.lease.LeaseWallet":{add:[95,2,1,""],advance:[95,2,1,""],get_lease:[95,2,1,""],get_lease_state:[95,2,1,""],on_lease_use_result:[95,2,1,""],remove:[95,2,1,""],set_client_name:[95,2,1,""]},"bosdyn.client.lease.LeaseWalletRequestProcessor":{get_lease_state:[95,2,1,""],mutate:[95,2,1,""]},"bosdyn.client.lease.LeaseWalletResponseProcessor":{mutate:[95,2,1,""]},"bosdyn.client.license":{LicenseClient:[96,1,1,""]},"bosdyn.client.license.LicenseClient":{default_service_name:[96,3,1,""],get_feature_enabled:[96,2,1,""],get_license_info:[96,2,1,""],service_type:[96,3,1,""]},"bosdyn.client.local_grid":{LocalGridClient:[97,1,1,""]},"bosdyn.client.local_grid.LocalGridClient":{default_service_name:[97,3,1,""],get_local_grid_types:[97,2,1,""],get_local_grid_types_async:[97,2,1,""],get_local_grids:[97,2,1,""],get_local_grids_async:[97,2,1,""],service_type:[97,3,1,""]},"bosdyn.client.log_annotation":{InvalidArgument:[98,4,1,""],LogAnnotationClient:[98,1,1,""],LogAnnotationHandler:[98,1,1,""]},"bosdyn.client.log_annotation.LogAnnotationClient":{add_log_blob:[98,2,1,""],add_log_blob_async:[98,2,1,""],add_log_protobuf:[98,2,1,""],add_log_protobuf_async:[98,2,1,""],add_operator_comment:[98,2,1,""],add_operator_comment_async:[98,2,1,""],add_text_messages:[98,2,1,""],add_text_messages_async:[98,2,1,""],default_service_name:[98,3,1,""],service_type:[98,3,1,""],update_from:[98,2,1,""]},"bosdyn.client.log_annotation.LogAnnotationHandler":{close:[98,2,1,""],emit:[98,2,1,""],fallback_log:[98,2,1,""],flush:[98,2,1,""],is_thread_alive:[98,2,1,""],record_level_to_proto_level:[98,2,1,""],record_to_msg:[98,2,1,""],restart:[98,2,1,""]},"bosdyn.client.manipulation_api_client":{ManipulationApiClient:[99,1,1,""]},"bosdyn.client.manipulation_api_client.ManipulationApiClient":{default_service_name:[99,3,1,""],manipulation_api_command:[99,2,1,""],manipulation_api_command_async:[99,2,1,""],manipulation_api_feedback_command:[99,2,1,""],manipulation_api_feedback_command_async:[99,2,1,""],service_type:[99,3,1,""],update_from:[99,2,1,""]},"bosdyn.client.math_helpers":{Quat:[100,1,1,""],SE2Pose:[100,1,1,""],SE2Velocity:[100,1,1,""],SE3Pose:[100,1,1,""],SE3Velocity:[100,1,1,""],angle_diff:[100,5,1,""],angle_diff_degrees:[100,5,1,""],is_within_threshold:[100,5,1,""],pose_to_xyz_yaw:[100,5,1,""],quat_to_eulerZYX:[100,5,1,""],recenter_angle:[100,5,1,""],skew_matrix_2d:[100,5,1,""],skew_matrix_3d:[100,5,1,""],transform_se2velocity:[100,5,1,""],transform_se3velocity:[100,5,1,""]},"bosdyn.client.math_helpers.Quat":{closest_yaw_only_quaternion:[100,2,1,""],from_matrix:[100,2,1,""],from_obj:[100,2,1,""],from_pitch:[100,2,1,""],from_roll:[100,2,1,""],from_yaw:[100,2,1,""],inverse:[100,2,1,""],mult:[100,2,1,""],normalize:[100,2,1,""],to_axis_angle:[100,2,1,""],to_matrix:[100,2,1,""],to_obj:[100,2,1,""],to_pitch:[100,2,1,""],to_proto:[100,2,1,""],to_roll:[100,2,1,""],to_yaw:[100,2,1,""],transform_point:[100,2,1,""],transform_vec3:[100,2,1,""]},"bosdyn.client.math_helpers.SE2Pose":{flatten:[100,2,1,""],from_matrix:[100,2,1,""],from_obj:[100,2,1,""],get_closest_se3_transform:[100,2,1,""],inverse:[100,2,1,""],mult:[100,2,1,""],position:[100,2,1,""],to_adjoint_matrix:[100,2,1,""],to_matrix:[100,2,1,""],to_obj:[100,2,1,""],to_proto:[100,2,1,""],to_rot_matrix:[100,2,1,""]},"bosdyn.client.math_helpers.SE2Velocity":{angular:[100,2,1,""],from_obj:[100,2,1,""],from_vector:[100,2,1,""],linear:[100,2,1,""],to_obj:[100,2,1,""],to_proto:[100,2,1,""],to_vector:[100,2,1,""]},"bosdyn.client.math_helpers.SE3Pose":{from_identity:[100,2,1,""],from_matrix:[100,2,1,""],from_obj:[100,2,1,""],from_se2:[100,2,1,""],get_closest_se2_transform:[100,2,1,""],get_translation:[100,2,1,""],inverse:[100,2,1,""],mult:[100,2,1,""],position:[100,2,1,""],rotation:[100,2,1,""],to_adjoint_matrix:[100,2,1,""],to_matrix:[100,2,1,""],to_obj:[100,2,1,""],to_proto:[100,2,1,""],transform_cloud:[100,2,1,""],transform_cloud_from_matrix:[100,2,1,""],transform_point:[100,2,1,""],transform_vec3:[100,2,1,""]},"bosdyn.client.math_helpers.SE3Velocity":{angular:[100,2,1,""],from_obj:[100,2,1,""],from_vector:[100,2,1,""],linear:[100,2,1,""],to_obj:[100,2,1,""],to_proto:[100,2,1,""],to_vector:[100,2,1,""]},"bosdyn.client.network_compute_bridge_client":{ExternalServerError:[101,4,1,""],ExternalServiceNotFoundError:[101,4,1,""],NetworkComputeBridgeClient:[101,1,1,""],NetworkComputeRotationError:[101,4,1,""]},"bosdyn.client.network_compute_bridge_client.NetworkComputeBridgeClient":{default_service_name:[101,3,1,""],list_available_models_command:[101,2,1,""],list_available_models_command_async:[101,2,1,""],network_compute_bridge_command:[101,2,1,""],network_compute_bridge_command_async:[101,2,1,""],service_type:[101,3,1,""]},"bosdyn.client.payload":{PayloadClient:[102,1,1,""]},"bosdyn.client.payload.PayloadClient":{default_service_name:[102,3,1,""],list_payloads:[102,2,1,""],list_payloads_async:[102,2,1,""],service_type:[102,3,1,""]},"bosdyn.client.payload_registration":{InvalidPayloadCredentialsError:[103,4,1,""],PayloadAlreadyExistsError:[103,4,1,""],PayloadDoesNotExistError:[103,4,1,""],PayloadNotAuthorizedError:[103,4,1,""],PayloadRegistrationClient:[103,1,1,""],PayloadRegistrationKeepAlive:[103,1,1,""],PayloadRegistrationResponseError:[103,4,1,""]},"bosdyn.client.payload_registration.PayloadRegistrationClient":{default_service_name:[103,3,1,""],get_payload_auth_token:[103,2,1,""],register_payload:[103,2,1,""],register_payload_async:[103,2,1,""],service_type:[103,3,1,""],update_payload_version:[103,2,1,""],update_payload_version_async:[103,2,1,""]},"bosdyn.client.payload_registration.PayloadRegistrationKeepAlive":{is_alive:[103,2,1,""],shutdown:[103,2,1,""],start:[103,2,1,""]},"bosdyn.client.point_cloud":{PointCloudClient:[104,1,1,""],PointCloudDataError:[104,4,1,""],PointCloudResponseError:[104,4,1,""],SourceDataError:[104,4,1,""],UnknownPointCloudSourceError:[104,4,1,""],build_pc_request:[104,5,1,""]},"bosdyn.client.point_cloud.PointCloudClient":{default_service_name:[104,3,1,""],get_point_cloud:[104,2,1,""],get_point_cloud_async:[104,2,1,""],get_point_cloud_from_sources:[104,2,1,""],get_point_cloud_from_sources_async:[104,2,1,""],list_point_cloud_sources:[104,2,1,""],list_point_cloud_sources_async:[104,2,1,""],service_type:[104,3,1,""]},"bosdyn.client.power":{BatteryMissingError:[105,4,1,""],CommandInProgressError:[105,4,1,""],CommandTimedOutError:[105,4,1,""],EstoppedError:[105,4,1,""],FaultedError:[105,4,1,""],PowerClient:[105,1,1,""],PowerError:[105,4,1,""],PowerResponseError:[105,4,1,""],ShorePowerConnectedError:[105,4,1,""],is_powered_on:[105,5,1,""],power_cycle_robot:[105,5,1,""],power_off:[105,5,1,""],power_off_motors:[105,5,1,""],power_off_payload_ports:[105,5,1,""],power_off_robot:[105,5,1,""],power_off_wifi_radio:[105,5,1,""],power_on:[105,5,1,""],power_on_motors:[105,5,1,""],power_on_payload_ports:[105,5,1,""],power_on_wifi_radio:[105,5,1,""],safe_power_off:[105,5,1,""]},"bosdyn.client.power.PowerClient":{default_service_name:[105,3,1,""],power_command:[105,2,1,""],power_command_async:[105,2,1,""],power_command_feedback:[105,2,1,""],power_command_feedback_async:[105,2,1,""],service_type:[105,3,1,""],update_from:[105,2,1,""]},"bosdyn.client.processors":{AddRequestHeader:[106,1,1,""]},"bosdyn.client.processors.AddRequestHeader":{mutate:[106,2,1,""]},"bosdyn.client.recording":{CouldNotCreateWaypointError:[107,4,1,""],EdgeExistsError:[107,4,1,""],EdgeMissingTransformError:[107,4,1,""],FiducialPoseError:[107,4,1,""],FollowingRouteError:[107,4,1,""],GraphNavRecordingServiceClient:[107,1,1,""],MapTooLargeLicenseError:[107,4,1,""],MissingFiducialsError:[107,4,1,""],NotLocalizedToEndError:[107,4,1,""],NotLocalizedToExistingMapError:[107,4,1,""],NotReadyYetError:[107,4,1,""],NotRecordingError:[107,4,1,""],RecordingServiceResponseError:[107,4,1,""],RemoteCloudFailureNoDataError:[107,4,1,""],RemoteCloudFailureNotInDirectoryError:[107,4,1,""],UnknownWaypointError:[107,4,1,""],WaypointRegion:[107,1,1,""]},"bosdyn.client.recording.GraphNavRecordingServiceClient":{create_edge:[107,2,1,""],create_edge_async:[107,2,1,""],create_waypoint:[107,2,1,""],create_waypoint_async:[107,2,1,""],default_service_name:[107,3,1,""],get_record_status:[107,2,1,""],get_record_status_async:[107,2,1,""],make_edge:[107,2,1,""],make_edge_environment:[107,2,1,""],make_recording_environment:[107,2,1,""],make_waypoint_environment:[107,2,1,""],service_type:[107,3,1,""],set_recording_environment:[107,2,1,""],set_recording_environment_async:[107,2,1,""],start_recording:[107,2,1,""],start_recording_async:[107,2,1,""],stop_recording:[107,2,1,""],stop_recording_async:[107,2,1,""]},"bosdyn.client.recording.WaypointRegion":{CIRCLE_REGION:[107,3,1,""],DEFAULT_REGION:[107,3,1,""],EMPTY_REGION:[107,3,1,""]},"bosdyn.client.robot":{Robot:[108,1,1,""],RobotError:[108,4,1,""],UnregisteredServiceError:[108,4,1,""],UnregisteredServiceNameError:[108,4,1,""],UnregisteredServiceTypeError:[108,4,1,""]},"bosdyn.client.robot.Robot":{authenticate:[108,2,1,""],authenticate_from_cache:[108,2,1,""],authenticate_from_payload_credentials:[108,2,1,""],authenticate_with_token:[108,2,1,""],ensure_channel:[108,2,1,""],ensure_client:[108,2,1,""],ensure_secure_channel:[108,2,1,""],get_cached_robot_id:[108,2,1,""],get_cached_usernames:[108,2,1,""],get_frame_tree_snapshot:[108,2,1,""],get_id:[108,2,1,""],has_arm:[108,2,1,""],is_estopped:[108,2,1,""],is_powered_on:[108,2,1,""],list_services:[108,2,1,""],operator_comment:[108,2,1,""],power_off:[108,2,1,""],power_on:[108,2,1,""],register_payload_and_authenticate:[108,2,1,""],setup_token_cache:[108,2,1,""],start_time_sync:[108,2,1,""],stop_time_sync:[108,2,1,""],sync_with_directory:[108,2,1,""],time_sec:[108,2,1,""],time_sync:[108,2,1,""],update_from:[108,2,1,""],update_user_token:[108,2,1,""]},"bosdyn.client.robot_command":{BehaviorFaultError:[109,4,1,""],CommandFailedError:[109,4,1,""],CommandTimedOutError:[109,4,1,""],Error:[109,4,1,""],ExpiredError:[109,4,1,""],NoTimeSyncError:[109,4,1,""],NotClearedError:[109,4,1,""],NotPoweredOnError:[109,4,1,""],RobotCommandBuilder:[109,1,1,""],RobotCommandClient:[109,1,1,""],RobotCommandResponseError:[109,4,1,""],TooDistantError:[109,4,1,""],UnknownFrameError:[109,4,1,""],UnsupportedError:[109,4,1,""],block_until_arm_arrives:[109,5,1,""],blocking_stand:[109,5,1,""]},"bosdyn.client.robot_command.RobotCommandBuilder":{arm_carry_command:[109,2,1,""],arm_gaze_command:[109,2,1,""],arm_pose_command:[109,2,1,""],arm_ready_command:[109,2,1,""],arm_stow_command:[109,2,1,""],arm_wrench_command:[109,2,1,""],battery_change_pose_command:[109,2,1,""],build_body_external_forces:[109,2,1,""],build_synchro_command:[109,2,1,""],claw_gripper_close_command:[109,2,1,""],claw_gripper_open_angle_command:[109,2,1,""],claw_gripper_open_command:[109,2,1,""],claw_gripper_open_fraction_command:[109,2,1,""],follow_arm_command:[109,2,1,""],freeze_command:[109,2,1,""],mobility_params:[109,2,1,""],safe_power_off_command:[109,2,1,""],selfright_command:[109,2,1,""],sit_command:[109,2,1,""],stance_command:[109,2,1,""],stand_command:[109,2,1,""],stop_command:[109,2,1,""],synchro_se2_trajectory_command:[109,2,1,""],synchro_se2_trajectory_point_command:[109,2,1,""],synchro_sit_command:[109,2,1,""],synchro_stand_command:[109,2,1,""],synchro_trajectory_command_in_body_frame:[109,2,1,""],synchro_velocity_command:[109,2,1,""],trajectory_command:[109,2,1,""],velocity_command:[109,2,1,""]},"bosdyn.client.robot_command.RobotCommandClient":{clear_behavior_fault:[109,2,1,""],clear_behavior_fault_async:[109,2,1,""],default_service_name:[109,3,1,""],robot_command:[109,2,1,""],robot_command_async:[109,2,1,""],robot_command_feedback:[109,2,1,""],robot_command_feedback_async:[109,2,1,""],service_type:[109,3,1,""],timesync_endpoint:[109,2,1,""],update_from:[109,2,1,""]},"bosdyn.client.robot_id":{RobotIdClient:[110,1,1,""],create_strict_version:[110,5,1,""]},"bosdyn.client.robot_id.RobotIdClient":{default_service_name:[110,3,1,""],get_id:[110,2,1,""],get_id_async:[110,2,1,""],service_type:[110,3,1,""]},"bosdyn.client.robot_state":{RobotStateClient:[111,1,1,""],has_arm:[111,5,1,""]},"bosdyn.client.robot_state.RobotStateClient":{default_service_name:[111,3,1,""],get_hardware_config_with_link_info:[111,2,1,""],get_robot_hardware_configuration:[111,2,1,""],get_robot_hardware_configuration_async:[111,2,1,""],get_robot_link_model:[111,2,1,""],get_robot_link_model_async:[111,2,1,""],get_robot_metrics:[111,2,1,""],get_robot_metrics_async:[111,2,1,""],get_robot_state:[111,2,1,""],get_robot_state_async:[111,2,1,""],service_type:[111,3,1,""]},"bosdyn.client.sdk":{Sdk:[112,1,1,""],SdkError:[112,4,1,""],UnableToLoadAppTokenError:[112,4,1,""],UnsetAppTokenError:[112,4,1,""],create_standard_sdk:[112,5,1,""],decode_token:[112,5,1,""],generate_client_name:[112,5,1,""],log_token_time_remaining:[112,5,1,""]},"bosdyn.client.sdk.Sdk":{create_robot:[112,2,1,""],load_app_token:[112,2,1,""],load_robot_cert:[112,2,1,""],register_service_client:[112,2,1,""],set_max_message_length:[112,2,1,""]},"bosdyn.client.server_util":{ResponseContext:[113,1,1,""]},"bosdyn.client.spot_cam":{audio:[115,0,0,"-"],compositor:[116,0,0,"-"],health:[117,0,0,"-"],lighting:[118,0,0,"-"],media_log:[119,0,0,"-"],network:[120,0,0,"-"],power:[121,0,0,"-"],ptz:[122,0,0,"-"],streamquality:[123,0,0,"-"],version:[124,0,0,"-"]},"bosdyn.client.spot_cam.audio":{AudioClient:[115,1,1,""]},"bosdyn.client.spot_cam.audio.AudioClient":{default_service_name:[115,3,1,""],delete_sound:[115,2,1,""],delete_sound_async:[115,2,1,""],get_volume:[115,2,1,""],get_volume_async:[115,2,1,""],list_sounds:[115,2,1,""],list_sounds_async:[115,2,1,""],load_sound:[115,2,1,""],play_sound:[115,2,1,""],play_sound_async:[115,2,1,""],service_type:[115,3,1,""],set_volume:[115,2,1,""],set_volume_async:[115,2,1,""]},"bosdyn.client.spot_cam.compositor":{CompositorClient:[116,1,1,""]},"bosdyn.client.spot_cam.compositor.CompositorClient":{default_service_name:[116,3,1,""],get_ir_colormap:[116,2,1,""],get_ir_colormap_async:[116,2,1,""],get_screen:[116,2,1,""],get_screen_async:[116,2,1,""],get_visible_cameras:[116,2,1,""],get_visible_cameras_async:[116,2,1,""],list_screens:[116,2,1,""],list_screens_async:[116,2,1,""],service_type:[116,3,1,""],set_ir_colormap:[116,2,1,""],set_ir_colormap_async:[116,2,1,""],set_ir_meter_overlay:[116,2,1,""],set_ir_meter_overlay_async:[116,2,1,""],set_screen:[116,2,1,""],set_screen_async:[116,2,1,""]},"bosdyn.client.spot_cam.health":{HealthClient:[117,1,1,""]},"bosdyn.client.spot_cam.health.HealthClient":{clear_bit_events:[117,2,1,""],clear_bit_events_async:[117,2,1,""],default_service_name:[117,3,1,""],get_bit_status:[117,2,1,""],get_bit_status_async:[117,2,1,""],get_temperature:[117,2,1,""],get_temperature_async:[117,2,1,""],service_type:[117,3,1,""]},"bosdyn.client.spot_cam.lighting":{LightingClient:[118,1,1,""]},"bosdyn.client.spot_cam.lighting.LightingClient":{default_service_name:[118,3,1,""],get_led_brightness:[118,2,1,""],get_led_brightness_async:[118,2,1,""],service_type:[118,3,1,""],set_led_brightness:[118,2,1,""],set_led_brightness_async:[118,2,1,""]},"bosdyn.client.spot_cam.media_log":{MediaLogClient:[119,1,1,""]},"bosdyn.client.spot_cam.media_log.MediaLogClient":{"delete":[119,2,1,""],default_service_name:[119,3,1,""],delete_async:[119,2,1,""],enable_debug:[119,2,1,""],enable_debug_async:[119,2,1,""],get_status:[119,2,1,""],get_status_async:[119,2,1,""],list_cameras:[119,2,1,""],list_cameras_async:[119,2,1,""],list_logpoints:[119,2,1,""],retrieve:[119,2,1,""],retrieve_raw_data:[119,2,1,""],service_type:[119,3,1,""],set_passphrase:[119,2,1,""],set_passphrase_async:[119,2,1,""],store:[119,2,1,""],store_async:[119,2,1,""],tag:[119,2,1,""],tag_async:[119,2,1,""]},"bosdyn.client.spot_cam.network":{NetworkClient:[120,1,1,""]},"bosdyn.client.spot_cam.network.NetworkClient":{default_service_name:[120,3,1,""],get_ice_configuration:[120,2,1,""],get_ice_configuration_async:[120,2,1,""],service_type:[120,3,1,""],set_ice_configuration:[120,2,1,""],set_ice_configuration_async:[120,2,1,""]},"bosdyn.client.spot_cam.power":{PowerClient:[121,1,1,""]},"bosdyn.client.spot_cam.power.PowerClient":{cycle_power:[121,2,1,""],cycle_power_async:[121,2,1,""],default_service_name:[121,3,1,""],get_power_status:[121,2,1,""],get_power_status_async:[121,2,1,""],service_type:[121,3,1,""],set_power_status:[121,2,1,""],set_power_status_async:[121,2,1,""]},"bosdyn.client.spot_cam.ptz":{PtzClient:[122,1,1,""],shift_pan_angle:[122,5,1,""]},"bosdyn.client.spot_cam.ptz.PtzClient":{default_service_name:[122,3,1,""],get_ptz_position:[122,2,1,""],get_ptz_position_async:[122,2,1,""],get_ptz_velocity:[122,2,1,""],get_ptz_velocity_async:[122,2,1,""],initialize_lens:[122,2,1,""],initialize_lens_async:[122,2,1,""],list_ptz:[122,2,1,""],list_ptz_async:[122,2,1,""],service_type:[122,3,1,""],set_ptz_position:[122,2,1,""],set_ptz_position_async:[122,2,1,""],set_ptz_velocity:[122,2,1,""],set_ptz_velocity_async:[122,2,1,""]},"bosdyn.client.spot_cam.streamquality":{StreamQualityClient:[123,1,1,""]},"bosdyn.client.spot_cam.streamquality.StreamQualityClient":{default_service_name:[123,3,1,""],get_stream_params:[123,2,1,""],get_stream_params_async:[123,2,1,""],service_type:[123,3,1,""],set_stream_params:[123,2,1,""],set_stream_params_async:[123,2,1,""]},"bosdyn.client.spot_cam.version":{VersionClient:[124,1,1,""]},"bosdyn.client.spot_cam.version.VersionClient":{default_service_name:[124,3,1,""],get_software_version:[124,2,1,""],get_software_version_async:[124,2,1,""],get_software_version_full:[124,2,1,""],get_software_version_full_async:[124,2,1,""],service_type:[124,3,1,""]},"bosdyn.client.spot_check":{CameraCalibrationCalibrationError:[125,4,1,""],CameraCalibrationInternalError:[125,4,1,""],CameraCalibrationPowerError:[125,4,1,""],CameraCalibrationResponseError:[125,4,1,""],CameraCalibrationRobotCommandError:[125,4,1,""],CameraCalibrationTargetNotCenteredError:[125,4,1,""],CameraCalibrationTimedOutError:[125,4,1,""],CameraCalibrationUserCanceledError:[125,4,1,""],SpotCheckCameraTimeoutError:[125,4,1,""],SpotCheckClient:[125,1,1,""],SpotCheckEndstopTimeoutError:[125,4,1,""],SpotCheckError:[125,4,1,""],SpotCheckGroundCheckError:[125,4,1,""],SpotCheckImuCheckError:[125,4,1,""],SpotCheckLoadcellTimeoutError:[125,4,1,""],SpotCheckNotSittingError:[125,4,1,""],SpotCheckPowerOnFailure:[125,4,1,""],SpotCheckResponseError:[125,4,1,""],SpotCheckStandFailureError:[125,4,1,""],SpotCheckTimedOutError:[125,4,1,""],SpotCheckUnexpectedPowerChangeError:[125,4,1,""],run_camera_calibration:[125,5,1,""],run_spot_check:[125,5,1,""]},"bosdyn.client.spot_check.SpotCheckClient":{camera_calibration_command:[125,2,1,""],camera_calibration_command_async:[125,2,1,""],camera_calibration_feedback:[125,2,1,""],camera_calibration_feedback_async:[125,2,1,""],default_service_name:[125,3,1,""],service_type:[125,3,1,""],spot_check_command:[125,2,1,""],spot_check_command_async:[125,2,1,""],spot_check_feedback:[125,2,1,""],spot_check_feedback_async:[125,2,1,""]},"bosdyn.client.time_sync":{InactiveThreadError:[126,4,1,""],NotEstablishedError:[126,4,1,""],TimeSyncClient:[126,1,1,""],TimeSyncEndpoint:[126,1,1,""],TimeSyncError:[126,4,1,""],TimeSyncThread:[126,1,1,""],TimedOutError:[126,4,1,""],robot_time_range_from_datetimes:[126,5,1,""],robot_time_range_from_nanoseconds:[126,5,1,""],timespec_to_robot_timespan:[126,5,1,""]},"bosdyn.client.time_sync.TimeSyncClient":{default_service_name:[126,3,1,""],get_time_sync_update:[126,2,1,""],get_time_sync_update_async:[126,2,1,""],service_type:[126,3,1,""]},"bosdyn.client.time_sync.TimeSyncEndpoint":{clock_identifier:[126,2,1,""],clock_skew:[126,2,1,""],establish_timesync:[126,2,1,""],get_new_estimate:[126,2,1,""],get_robot_time_converter:[126,2,1,""],has_established_time_sync:[126,2,1,""],response:[126,2,1,""],robot_timestamp_from_local_secs:[126,2,1,""],round_trip_time:[126,2,1,""]},"bosdyn.client.time_sync.TimeSyncThread":{DEFAULT_TIME_SYNC_INTERVAL_SEC:[126,3,1,""],TIME_SYNC_SERVICE_NOT_READY_INTERVAL_SEC:[126,3,1,""],endpoint:[126,2,1,""],get_robot_clock_skew:[126,2,1,""],get_robot_time_converter:[126,2,1,""],has_established_time_sync:[126,2,1,""],robot_timestamp_from_local_secs:[126,2,1,""],should_exit:[126,2,1,""],start:[126,2,1,""],stop:[126,2,1,""],stopped:[126,2,1,""],thread_exception:[126,2,1,""],time_sync_interval_sec:[126,2,1,""],wait_for_sync:[126,2,1,""]},"bosdyn.client.token_cache":{ClearFailedError:[127,4,1,""],NotInCacheError:[127,4,1,""],TokenCache:[127,1,1,""],TokenCacheError:[127,4,1,""],TokenCacheFilesystem:[127,1,1,""],WriteFailedError:[127,4,1,""],atomic_file_write:[127,5,1,""]},"bosdyn.client.token_cache.TokenCache":{clear:[127,2,1,""],match:[127,2,1,""],read:[127,2,1,""],write:[127,2,1,""]},"bosdyn.client.token_cache.TokenCacheFilesystem":{clear:[127,2,1,""],match:[127,2,1,""],read:[127,2,1,""],write:[127,2,1,""]},"bosdyn.client.token_manager":{TokenManager:[128,1,1,""]},"bosdyn.client.token_manager.TokenManager":{is_alive:[128,2,1,""],stop:[128,2,1,""],update:[128,2,1,""]},"bosdyn.client.util":{DedupLoggingMessages:[129,1,1,""],GrpcServiceRunner:[129,1,1,""],add_base_arguments:[129,5,1,""],add_common_arguments:[129,5,1,""],add_payload_credentials_arguments:[129,5,1,""],add_service_endpoint_arguments:[129,5,1,""],add_service_hosting_arguments:[129,5,1,""],cli_auth:[129,5,1,""],cli_login_prompt:[129,5,1,""],default_app_token_path:[129,5,1,""],does_dedup_filter_exist:[129,5,1,""],get_bytes_field_whitelist:[129,5,1,""],get_logger:[129,5,1,""],populate_response_header:[129,5,1,""],setup_logging:[129,5,1,""],strip_get_image_response:[129,5,1,""],strip_image_response:[129,5,1,""],strip_large_bytes_fields:[129,5,1,""],strip_local_grid_responses:[129,5,1,""],strip_log_annotation:[129,5,1,""],strip_record_data_blob:[129,5,1,""],strip_record_signal_tick:[129,5,1,""],strip_store_data_request:[129,5,1,""],strip_store_image_request:[129,5,1,""]},"bosdyn.client.util.DedupLoggingMessages":{filter:[129,2,1,""]},"bosdyn.client.util.GrpcServiceRunner":{run_until_interrupt:[129,2,1,""],stop:[129,2,1,""]},"bosdyn.client.world_object":{WorldObjectClient:[130,1,1,""],make_add_world_object_req:[130,5,1,""],make_change_world_object_req:[130,5,1,""],make_delete_world_object_req:[130,5,1,""]},"bosdyn.client.world_object.WorldObjectClient":{default_service_name:[130,3,1,""],list_world_objects:[130,2,1,""],list_world_objects_async:[130,2,1,""],mutate_world_objects:[130,2,1,""],mutate_world_objects_async:[130,2,1,""],service_type:[130,3,1,""],timesync_endpoint:[130,2,1,""],update_from:[130,2,1,""]},"bosdyn.geometry":{EulerZXY:[150,1,1,""],to_euler_zxy:[150,5,1,""]},"bosdyn.geometry.EulerZXY":{to_quaternion:[150,2,1,""]},"bosdyn.mission":{client:[153,0,0,"-"],constants:[154,0,0,"-"],exceptions:[155,0,0,"-"],remote_client:[156,0,0,"-"],server_util:[157,0,0,"-"],util:[158,0,0,"-"]},"bosdyn.mission.client":{CompilationError:[153,4,1,""],InvalidAnswerCode:[153,4,1,""],InvalidQuestionId:[153,4,1,""],MissionClient:[153,1,1,""],MissionResponseError:[153,4,1,""],NoMissionError:[153,4,1,""],NoMissionPlayingError:[153,4,1,""],QuestionAlreadyAnswered:[153,4,1,""],ValidationError:[153,4,1,""]},"bosdyn.mission.client.MissionClient":{answer_question:[153,2,1,""],answer_question_async:[153,2,1,""],default_service_name:[153,3,1,""],get_info:[153,2,1,""],get_info_async:[153,2,1,""],get_mission:[153,2,1,""],get_mission_async:[153,2,1,""],get_state:[153,2,1,""],get_state_async:[153,2,1,""],load_mission:[153,2,1,""],load_mission_async:[153,2,1,""],pause_mission:[153,2,1,""],pause_mission_async:[153,2,1,""],play_mission:[153,2,1,""],play_mission_async:[153,2,1,""],restart_mission:[153,2,1,""],restart_mission_async:[153,2,1,""],service_type:[153,3,1,""],timesync_endpoint:[153,2,1,""],update_from:[153,2,1,""]},"bosdyn.mission.constants":{Result:[154,1,1,""]},"bosdyn.mission.constants.Result":{ERROR:[154,3,1,""],FAILURE:[154,3,1,""],RUNNING:[154,3,1,""],SUCCESS:[154,3,1,""]},"bosdyn.mission.exceptions":{CompileError:[155,4,1,""],Error:[155,4,1,""],InaccessibleParameterError:[155,4,1,""],MessageOverrideError:[155,4,1,""],MissingParameterError:[155,4,1,""],NodeUnreferenceableError:[155,4,1,""],UnknownType:[155,4,1,""],ValidationError:[155,4,1,""]},"bosdyn.mission.remote_client":{Error:[156,4,1,""],InvalidSessionId:[156,4,1,""],MissingInputs:[156,4,1,""],MissingLeases:[156,4,1,""],RemoteClient:[156,1,1,""],tree_status_from_tick_status:[156,5,1,""]},"bosdyn.mission.remote_client.RemoteClient":{default_service_name:[156,3,1,""],establish_session:[156,2,1,""],establish_session_async:[156,2,1,""],service_type:[156,3,1,""],stop:[156,2,1,""],stop_async:[156,2,1,""],teardown_session:[156,2,1,""],teardown_session_async:[156,2,1,""],tick:[156,2,1,""],tick_async:[156,2,1,""]},"bosdyn.mission.server_util":{ResponseContext:[157,1,1,""],set_response_header:[157,5,1,""]},"bosdyn.mission.util":{Error:[158,4,1,""],InvalidConversion:[158,4,1,""],ResultFromProto:[158,1,1,""],field_desc_to_pb_type:[158,5,1,""],get_value_from_constant_value_message:[158,5,1,""],get_value_from_value_message:[158,5,1,""],is_string_identifier:[158,5,1,""],most_restrictive_travel_params:[158,5,1,""],node_spec_to_short_string:[158,5,1,""],one_line_str:[158,5,1,""],proto_enum_to_result_constant:[158,5,1,""],proto_from_tuple:[158,5,1,""],python_type_to_pb_type:[158,5,1,""],python_var_to_value:[158,5,1,""],result_constant_to_proto_enum:[158,5,1,""],safe_pb_enum_to_string:[158,5,1,""],safe_pb_type_to_string:[158,5,1,""],tree_to_string:[158,5,1,""]},"bosdyn.mission.util.ResultFromProto":{proto_from_results:[158,3,1,""],results_from_proto:[158,3,1,""]},"bosdyn.util":{DatetimeParseError:[151,4,1,""],RobotTimeConverter:[151,1,1,""],distance_str:[151,5,1,""],duration_str:[151,5,1,""],duration_to_seconds:[151,5,1,""],format_metric:[151,5,1,""],now_nsec:[151,5,1,""],now_timestamp:[151,5,1,""],nsec_to_timestamp:[151,5,1,""],parse_datetime:[151,5,1,""],parse_timespan:[151,5,1,""],sec_to_nsec:[151,5,1,""],seconds_to_duration:[151,5,1,""],seconds_to_timestamp:[151,5,1,""],secs_to_hms:[151,5,1,""],set_timestamp_from_datetime:[151,5,1,""],set_timestamp_from_now:[151,5,1,""],set_timestamp_from_nsec:[151,5,1,""],timestamp_str:[151,5,1,""],timestamp_to_datetime:[151,5,1,""],timestamp_to_nsec:[151,5,1,""],timestamp_to_sec:[151,5,1,""]},"bosdyn.util.RobotTimeConverter":{convert_timestamp_from_local_to_robot:[151,2,1,""],robot_timestamp_from_local:[151,2,1,""],robot_timestamp_from_local_nsecs:[151,2,1,""],robot_timestamp_from_local_secs:[151,2,1,""]},bosdyn:{geometry:[150,0,0,"-"],util:[151,0,0,"-"]}},objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","method","Python method"],"3":["py","attribute","Python attribute"],"4":["py","exception","Python exception"],"5":["py","function","Python function"]},objtypes:{"0":"py:module","1":"py:class","2":"py:method","3":"py:attribute","4":"py:exception","5":"py:function"},terms:{"000":56,"0001":61,"00044be03a91":209,"0012627621181309223":31,"002":61,"003905495163053274":4,"005":61,"00675":43,"009":204,"010":61,"0126167":43,"014":61,"0166667":43,"01t00":176,"020":61,"022":61,"028":56,"045":43,"04t00":176,"0784593":64,"095":43,"0x00":24,"0x01":24,"0x02":24,"0xff":24,"100":[19,28,53,56,57,62,64,74,93],"1000":64,"1000base":[4,48],"1048576":115,"1056":61,"1059951782226562":4,"109":204,"10cm":64,"10m":173,"10ma":48,"110":206,"1100":4,"1196":174,"11ea":209,"120":4,"1200":125,"1234":175,"127":[51,64],"130":8,"13a":48,"13m":43,"146mm":19,"14875":57,"1500":64,"150uf":48,"150w":[4,48],"15100":51,"158":43,"1581869515":[173,177],"1583941992":61,"1583953617":61,"1585258226":61,"1585258227":61,"1585323526":61,"1585324337":61,"160":[4,24,64,133],"1601524800":33,"1601611200":33,"168":[43,44,50,51,55,57,58,59,60,61,112,175,204,206],"16th":[26,28],"180":[4,29,45,64,125,191],"1800":8,"189":60,"190mm":47,"191":4,"192":[43,44,50,51,55,57,58,59,60,61,112,175,204,206],"19557":[6,64],"195570":64,"19904":[60,61],"1e6":62,"1hz":48,"1min":64,"200":[56,61],"20000":[43,56],"20022":[43,44,51],"20080":43,"20180414":[60,61],"2019":[60,61],"20190601":64,"2020":[31,33,47,54,60,61,173,176,177,204],"20200120":[173,177],"20200120_120000":[173,177],"20201030":177,"20201031":177,"20201031_115000":177,"20201031_115950":177,"20201107":[173,177],"20201108":[173,177],"2021":[57,204],"2022":173,"20443":43,"2048":145,"20c":4,"20m":173,"21000":[43,51],"21174180507659912":4,"21443":[50,51],"217":64,"21900":44,"21mm":46,"22000":43,"224952738":61,"224990830":61,"246":61,"250":28,"255":[2,43,57,58,64,206],"256":[173,177],"259383":[60,61],"25mm":49,"26704":57,"275":61,"276":61,"280242100":61,"283":60,"28906":61,"290":61,"29516":61,"29t18":31,"29t183020z":31,"29t185610z":31,"29t185610z_inspect":31,"2cm":64,"2step":29,"2x1":100,"300":[4,56,57],"30000":43,"30022":43,"30080":43,"300uf":48,"30443":43,"30s":62,"30x":64,"31000":43,"312":55,"32000":43,"323":56,"3339":176,"338":204,"339":204,"340":[56,204],"352":56,"357":55,"35v":48,"360":[4,26,41,62,64,122,224],"3600":33,"363":61,"364":61,"36h11":64,"36m":189,"37m":189,"384":[56,127],"3mm":[46,49],"3rd":204,"3x1":100,"3x3":100,"400":[28,55],"400w":4,"401":64,"403":33,"427":55,"441":55,"443":[43,61],"450":28,"45c":4,"480":55,"491d":43,"4mb":[39,64],"4mm":49,"4x4":[37,100,138],"500":[4,58],"50051":[57,59,202],"50w":8,"54d4":209,"5708":64,"571":61,"577":204,"585":204,"5900":51,"5901":51,"59vabsolut":48,"5cm":64,"5gb":16,"5mm":46,"5ppm":48,"5ppmpp":48,"5x5":55,"6006":56,"600w":48,"602":204,"605":4,"60cm":19,"610":61,"615":61,"616":61,"616570624":61,"63mm":46,"640":55,"640x640":56,"644209920":61,"650":61,"695":204,"6x1":100,"6x6":[64,100],"707":59,"7071":6,"729":61,"72v":48,"72vmax":48,"738928c4410c":43,"786897121z":31,"78b076a2":43,"802":4,"80v":48,"8212":59,"826":61,"840":4,"854":56,"86274254322052":4,"8836":88,"8bit":[2,64],"8ien":196,"8mm":49,"90490007":[60,61],"9365":209,"9815ea67e2122dfd3eb2003716add29987e7daa1":56,"984":8,"9903":[60,61],"996009984":61,"9969173":64,"998":221,"abstract":[0,39,64,75,94],"boolean":[2,26,28,40,41,64,78,80,92,94,107,109,116,126,129,189],"break":[2,32,55,56,57,58,59,64],"byte":[2,24,41,62,64,80,81,82,129,137,141,147,149],"case":[9,17,18,19,23,26,32,35,36,39,40,47,53,56,58,60,61,62,64,76,85,91,93,103,108,109,201,206,207,215],"catch":[23,62,80],"class":[24,34,36,39,41,54,55,56,57,60,61,62,64,67,69,70,71,74,75,76,77,79,80,81,82,83,84,85,86,87,88,90,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,153,154,156,157,158,176,210,212],"const":[21,29,64],"default":[2,6,26,28,29,33,39,41,43,44,50,51,53,55,57,58,60,61,62,74,76,80,82,85,92,93,94,95,98,103,104,107,108,109,112,127,129,138,148,151,160,173,174,176,189,191,202,204,206,207,210,212,215,217,219,221],"enum":[2,13,19,26,28,33,39,53,62,64,80,88,95,107,154,158],"export":[57,58,59,174,210],"final":[3,15,21,24,55,56,61,62,64,92,196,221],"float":[2,24,41,58,64,78,80,94,105,108,109,116,125,126,130,138,151],"function":[18,26,28,32,34,37,38,39,42,48,55,57,58,59,64,76,80,93,94,95,104,105,108,109,111,125,129,138,151,174,176,188,201,206,207,213,215,220,224],"goto":[21,64],"import":[10,20,23,33,35,36,41,44,50,55,56,57,58,60,61,62,64,175],"int":[2,24,41,57,58,64,74,77,78,79,80,82,92,93,94,112,126,129,137,138,139,141,144,146,147,151,153],"long":[2,3,14,19,20,23,28,29,35,36,41,56,57,58,59,61,62,64,80,164],"new":[2,5,6,14,15,16,18,20,21,26,29,32,33,34,35,36,37,39,40,43,44,50,51,53,55,56,57,58,59,61,64,71,80,85,88,90,92,95,103,108,129,138,139,176,203,204,206,207,215],"null":51,"public":[1,63,64,89],"return":[2,4,6,10,14,15,20,21,23,26,28,29,31,33,38,39,40,41,48,50,53,55,57,58,59,60,61,62,64,69,71,73,74,76,77,78,79,80,81,84,85,86,87,88,90,91,92,93,94,95,97,99,100,101,102,103,104,105,107,108,109,110,111,112,119,124,125,126,127,129,130,133,134,137,138,139,141,142,144,145,146,147,148,149,151,158,177,201,202,204,207,215,220],"short":[28,29,35,39,40,41,48,61,62,64,177,196],"static":[21,23,37,39,64,76,92,94,95,98,100,107,109,112,139,153,206],"super":[57,64,95],"switch":[18,20,62,64,201],"throw":[2,58,59,61,62,94,125,158,206],"transient":[61,62,89],"true":[2,21,28,29,41,44,55,56,57,58,59,61,64,67,73,75,78,80,85,88,91,92,94,95,105,107,108,109,111,116,119,126,129,134,149,151,158,189],"try":[6,8,10,18,20,23,44,56,58,59,60,62,64,76,80,89,92,107,108,109,111,112,121,173,176,179,204,206,221],"var":[21,64,158],"while":[2,6,7,8,10,12,14,18,20,21,23,24,26,29,31,36,38,39,40,41,45,55,56,57,58,59,60,62,64,80,88,92,105,107,109,125,160,176,179,180,189,196,200,215,219],AWS:62,Added:62,Adding:177,And:[28,50,60,64,174,180],Are:[60,85,103],BPS:64,But:60,DNS:[39,64],For:[2,3,4,6,7,9,10,13,15,18,19,20,21,23,24,26,28,31,32,33,37,38,39,40,41,44,45,46,50,53,55,57,58,59,60,61,62,64,67,71,80,82,87,88,90,92,93,95,99,100,101,105,107,109,110,111,115,116,117,118,119,120,121,122,123,124,127,128,130,153,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,189,190,191,196,198,202,204,206,207,210,212,215,221,224],GPS:[16,18,31,32,41,176,197],Has:[24,62],ICE:[64,120],IDE:60,IDs:[14,17,36,64,80,196],LTS:[54,60,62],MKS:53,NOT:[6,20,35,64],Near:26,Not:[28,39,48,51,56,58,61,64],OLED:206,One:[2,18,26,31,35,60,62,64,92,95,107,211],PPS:8,THE:176,That:[21,55,64,160,161,162,163,164,165,166,167,168,169,170,171,172,198,210,212,224],The:[0,2,3,4,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,23,24,25,26,27,28,29,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,64,65,67,68,69,70,71,74,76,77,78,79,80,82,84,85,86,87,88,89,90,91,92,93,94,95,98,99,100,101,103,104,105,107,108,109,111,125,126,128,129,130,136,137,148,151,152,153,155,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,180,181,182,183,184,185,186,187,188,189,191,196,197,198,201,202,204,205,206,207,209,210,211,212,215,217,219,221,223,224],Their:53,Then:[2,7,26,29,44,55,60,64,161,198,207,215],There:[6,14,18,23,24,26,28,31,34,35,36,39,40,41,50,55,56,60,61,62,64,92,95,108,109,153,162,165,171,173,176,189,196,202,204,217,221],These:[0,1,2,6,16,23,24,28,32,33,34,36,37,39,40,41,43,44,50,56,61,62,63,64,100,109,173,176,177,178,179,189,195,196,202,206,207,209,210,212,213,222,223],UIs:64,USING:210,Use:[16,26,36,43,50,51,53,55,56,57,58,60,62,64,72,80,92,95,109,179,196,200,204,206,213,221],Used:[62,64],Useful:[64,88],Uses:[2,17,62,64,74],Using:[19,23,37,53,61,62,64,91,95,103,160,161,162,163,164,166,167,168,169,170,172,189,194,196,197,201,208,215,219,220,221],WAS:64,WILL:176,Will:[29,64,88,92,107,179],With:[6,23,29,38,59,60,62,176,186],__call__:61,__init__:[57,206],__main__:[55,57,58,60,61],__name__:[55,57,58],_build_auth_request:39,_channel:61,_end_unary_response_block:61,_endpoint:88,_error_from_respons:39,_handler:98,_inactiverpcerror:61,_label_:[57,58],_max_x_spe:189,_max_y_spe:189,_movement_on:189,_setaccesspoint:206,_standup:189,_stub:39,_token_from_respons:39,_unique_id:88,a099:43,a_adjoint_b:100,a_adjoint_b_matrix:100,a_tform_b:100,a_tform_c:100,abc:94,abil:[6,17,26,40,43,47,62,64],abl:[6,20,21,26,33,36,38,39,40,41,43,57,61,62,64,80,191,196,207,221],abort:[62,64,92],about:[2,3,7,11,12,14,15,16,23,24,26,28,29,33,35,36,37,40,41,42,43,46,50,55,57,58,60,61,62,64,77,79,84,85,95,112,126,153,161,164,177,178,181,182,192,210,215],abov:[2,4,19,21,23,26,32,37,39,44,45,47,50,51,55,56,57,58,59,60,61,62,64,129,174,175,189,198,200,202,206,207,219],abruptli:40,absolut:[2,28,29,62,153,217],absolute_path_to_class:210,absolute_path_to_pb:210,absorb:45,acceler:[4,6,64],accept:[28,39,43,49,56,57,58,62,64,76,109,155,207,210,212],accepted_answer_cod:64,access:[9,12,17,20,21,23,24,26,33,36,39,40,42,43,44,50,51,55,57,61,62,64,74,80,84,85,88,89,94,95,97,100,103,108,109,110,129,133,138,139,147,191,206,210,221],accessor:[67,108,109,130,142,153],accessori:8,accident:[41,62],accommod:48,accompani:95,accomplish:[6,19,26,62,70,159,194],accord:[64,80],accordingli:45,account:[23,39,50,62,174],accross:64,accumul:18,accur:[29,37,41,62,64,107,169],accuraci:[7,48,56,64,109],achiev:[2,18,28,50,62,64,108,126,189],acquaint:159,acquir:[32,33,40,41,61,62,64,71,75,77,78,79,85,94,95,96,103,109,201,210,215,220,224],acquire_and_process_request:78,acquire_async:95,acquire_data:[31,77],acquire_data_async:77,acquire_data_error:77,acquire_plugin_data:79,acquire_plugin_data_async:79,acquire_response_fn:[41,80],acquiredata:[32,64,77,78,79,80,215],acquiredatarequest:[31,32],acquireleas:64,acquireplugindata:[32,41,64,80],acquireplugindatarequest:[41,80],acquireplugindatarespons:[41,80],acquisit:[30,34,37,40,44,63,64,67,68,75,159,206],acquisition_request:[41,64,77,78,79],acquisition_tim:[37,64],acquisition_time_second:41,acquisition_timestamp:64,acquisitionrequestlist:[77,78,79],acquist:64,across:[16,18,23,39,40,41,44,45,57,64,103,206,207],act:[10,26,39,40,61,64,108],action:[9,16,18,22,26,31,34,41,51,53,57,58,62,70,77,78,79,81,130,204],action_add:64,action_chang:64,action_delet:64,action_id:[31,41,64,79,81],action_nam:[31,32,64,77,78],action_unknown:64,activ:[8,10,17,21,26,29,36,39,40,41,55,56,57,58,60,61,62,64,75,90,92,95,125,210,215],active_config:64,actual:[27,28,60,64,91,98,204,219],actuat:[4,35,40,48,62,64],acycl:64,adapt:[17,50,214],add:[0,6,21,26,33,37,40,44,51,55,57,58,59,60,62,64,70,75,76,82,95,98,100,108,113,129,130,138,139,145,174,196,202,207,210,212,221,222],add_app_token:74,add_argu:[55,57,58],add_base_argu:129,add_blob:[62,82],add_blob_async:82,add_common_argu:[55,57,58,129],add_done_callback:76,add_edge_to_tre:[37,91],add_error:[41,80],add_ev:82,add_events_async:82,add_image_coordin:62,add_imageserviceservicer_to_serv:34,add_insecure_port:57,add_lease_wallet_processor:95,add_log_blob:98,add_log_blob_async:98,add_log_protobuf:98,add_log_protobuf_async:98,add_message_seri:138,add_networkcomputebridgeworkerservicer_to_serv:57,add_operator_com:[82,98],add_operator_comment_async:[82,98],add_payload_credentials_argu:129,add_pod_seri:138,add_proto_read:142,add_protobuf:[62,82],add_protobuf_async:82,add_request:80,add_sav:80,add_seri:[138,139],add_series_descriptor:139,add_service_endpoint_argu:129,add_service_hosting_argu:129,add_servicer_to_server_fn:[34,129],add_signal_tick:82,add_signal_tick_async:82,add_task:70,add_text_messag:[82,98],add_text_messages_async:[82,98],added:[5,9,20,26,29,31,37,40,45,49,62,64,80,108,109,129,130,138,139,173,177,189,196,201,207],adding:[26,38,62,64,82,83,98,130,191],addit:[0,18,20,24,25,33,34,37,40,41,44,51,53,59,60,61,64,74,78,80,112,113,138,139,145,148,159,204,206,207,208,215,221],addition:[24,25,26,27,28,29,41,62,64,76,108,189,191,196,206,208],additional_index:[64,138,139,148],additional_index_nam:[64,138,139,148],additional_param:78,additional_properti:[57,58,64],addlogannot:64,addlogannotationrespons:62,addrequesthead:106,address:[24,27,33,36,39,43,50,51,55,57,59,61,62,64,73,74,75,76,85,112,120,173,176,202,204,206,207,209,221],addserieserror:136,aded:64,adequ:[48,207],adjac:[18,47,64],adjoint:100,adjust:[17,21,23,26,28,29,50,62,64,189],adjust_paramet:64,admin:[23,43,50,51,60,64,207],administr:[51,60,62,64],admiss:2,admitt:64,admittance_setting_loos:64,admittance_setting_norm:64,admittance_setting_off:64,admittance_setting_stiff:64,admittance_setting_unknown:64,admittance_setting_very_stiff:64,adopt:[37,67,76,77,79,81,82,83,86,92,98,105,130,153,176],advanc:[25,26,27,39,50,56,64,95,109],advantag:[6,36,58,59,85,208],advers:62,adversari:39,advertis:[101,215],advis:64,aerial:64,affect:[2,26,36,40,50,62,64,210],after:[2,6,7,15,18,19,23,26,27,28,29,33,36,39,40,43,44,50,51,53,55,56,57,58,59,60,61,62,64,76,80,85,88,92,100,103,109,126,130,161,176,191,195,202,207,210,215,221,224],afterward:62,again:[50,56,58,59,60,61,62,64,92,107,153,204,211],against:[7,19,21,26,35,56,64],aggreg:[32,64,176],agil:47,ago:[57,61,173,177],agre:53,ahead:2,aid:[13,18,62,76],aim:21,air:[2,64],aka:[62,64],algebra:59,algorithm:[19,56,64,125],align:[2,6,7,37,38,40,58,59,61,64,91,100,107,109,161,202],aliv:[34,36,40,57,62,64,85,95,98,103,125,176,221],all:[0,2,6,21,22,23,24,26,28,29,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,47,48,50,51,53,54,56,57,58,59,60,61,62,64,75,76,77,78,79,80,84,85,89,90,91,93,94,95,98,101,102,104,106,108,109,111,112,120,122,124,129,130,139,149,153,160,173,176,177,183,188,189,191,196,198,201,203,204,206,207,209,210,211,212,213,215,221],alloc:33,allot:89,allow:[2,3,5,6,7,10,12,15,17,18,20,21,24,26,28,29,32,33,35,38,40,41,43,44,48,50,51,59,61,62,64,74,82,84,85,88,89,94,96,100,102,103,104,107,109,129,165,171,175,176,196,201,202,204,206,208,210,211,212,215,221,224],allow_async:88,allow_degraded_percept:64,allowable_orient:[58,64],almost:[53,56],alon:64,along:[7,9,10,17,18,22,24,33,49,53,56,57,59,62,64,92,109,189,201,206],alongsid:[10,26,62,64],alpha:[2,64],alreadi:[7,14,21,28,36,38,44,56,57,58,59,61,62,64,80,85,90,92,95,98,103,107,108,109,115,122,126,129,134,138,139,153,207,221],also:[0,5,6,8,10,16,17,18,20,21,23,26,28,29,32,33,36,37,39,40,41,42,44,45,48,50,51,53,55,59,60,61,62,64,85,92,95,108,109,112,175,188,189,196,197,204,206,207,210,212,218,221,224],alter:[2,28],altern:[15,62,64,175,221],alternate_robot_tform_waypoint:64,although:[23,33,39],aluminum:49,alwai:[2,8,21,26,28,29,36,37,41,53,57,58,59,60,62,64,129,158,175],always_print_logger_level:129,always_reprompt:64,always_restart:[21,64,158],amazon:[62,174],ambigu:[62,64,105],ambl:[29,62,64,224],among:[64,100],amount:[2,10,17,20,32,33,39,40,56,58,62,64,94,169,210,212],amp:[64,201,220,224],amplitud:[2,29],anaconda:60,analysi:[22,33,37,64],ang_i:100,ang_interp_cubic_eul:64,ang_interp_linear:64,ang_interp_unknown:64,ang_interpol:64,ang_x:100,ang_z:100,angl:[2,6,26,29,59,64,92,100,122,150],angle_diff:100,angle_diff_degre:100,angular:[6,31,53,59,62,64,100],angular_velocity_of_hand_rt_odom_in_hand:64,ani:[2,3,5,6,8,9,10,13,14,18,19,20,21,22,24,26,27,28,29,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,49,51,55,57,58,60,61,62,64,76,77,79,80,82,85,88,90,92,94,95,98,101,103,107,108,109,113,125,126,129,151,155,176,189,191,196,201,204,207,210,212,215,221,223,224],anim:2,annot:[13,16,17,20,43,55,56,57,58,59,62,63,68,107,133,134,138,139,145,148,199,223],annotation_state_non:64,annotation_state_set:64,annotation_state_unknown:64,announc:[39,43,64,221],announce_cli:207,announce_servic:207,anoth:[13,16,17,18,19,21,37,39,57,61,62,64,69,87,99,109,121,125,173,189,197,202,204,210,212],another_channel:31,answer:[21,62,64,120,153,181],answer_cod:64,answer_quest:153,answer_question_async:153,answered_quest:64,answerquest:[21,64],anymor:62,anyon:[10,26],anyth:[19,36,41,58,59,64,80],anywher:[21,26],apart:[2,26,29],api:[0,1,2,6,7,9,11,12,13,15,17,18,19,20,21,22,23,24,25,27,30,33,36,37,38,39,41,48,55,56,57,58,59,60,61,63,64,66,67,68,69,71,73,77,78,79,80,81,82,83,84,85,86,87,88,89,90,92,93,94,95,96,97,98,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,129,130,131,132,133,151,153,156,158,159,160,161,162,163,164,166,167,168,169,170,172,173,177,178,179,180,182,185,189,191,193,194,196,202,203,205,206,208,213,215,217,221],api_grasp_overrid:64,api_vers:64,apitest:189,app:[18,20,22,50,62,74,89,112,129,196,206],app_token:[39,71,74],appear:[23,26,36,40,41,43,50,55,61,62,64,92,204,206,221],append:[26,33,43,57,58,62,64,78,91,92,95,221,223],appl:202,appli:[6,7,28,40,50,62,64,85],applic:[0,3,12,13,17,18,22,23,25,26,27,28,36,37,38,39,40,41,42,43,50,51,52,54,57,61,62,64,71,74,79,88,95,108,129,174,176,186,189,199,204,206,210,211,221,223],application_token:[39,64],application_token_requir:85,approach:[18,23,26,39,53,64],appropri:[6,26,33,37,40,48,62,64,80,93,94,129,158],approxim:[13,55,58,64],april:[19,64,189,197],aprilrobot:189,apriltag:[16,19,37,40,62,64],apriltag_properti:64,apt:[51,175,210,218,224],arbitrari:[2,24,29,33,38,40,62,64,81,127,177,200,207],arbitrarili:[28,36],arc:164,architect:32,architectur:[23,41,61],area:[10,13,14,16,18,19,26,36,38,45,47,55,62,213],aren:[19,55,58,64,191],arg:[39,57,58,75,98,109,143,148,157],argpars:[55,57,58],arguement:64,argument:[26,27,31,39,41,44,51,55,57,58,59,60,61,62,64,71,75,76,80,82,83,88,89,95,98,102,103,104,109,116,129,165,174,175,176,177,189,191,193,196,204,206,208,210,212,213,215,217,219,221,224],argumentpars:[55,57,58],argv:[55,57,58],aris:[36,43,62],arm:[2,3,26,28,42,55,58,59,63,64,68,108,109,111,159,162,165,171],arm_and_mobility_command:160,arm_carry_command:[58,109],arm_cartesian_command:64,arm_cartesian_feedback:64,arm_command:[6,59,109],arm_command_feedback:64,arm_door:161,arm_drag_command:64,arm_drag_feedback:64,arm_gaz:163,arm_gaze_command:[64,109],arm_gaze_feedback:64,arm_grasp:[58,165],arm_joint_mov:166,arm_joint_move_command:64,arm_joint_move_feedback:64,arm_move_frame_bodi:2,arm_move_frame_center_of_footprint:2,arm_move_frame_hand:2,arm_move_frame_shadow:2,arm_move_frame_should:2,arm_move_frame_unknown:2,arm_move_param:2,arm_pose_command:[59,109],arm_ready_command:109,arm_simpl:167,arm_stop_command:64,arm_stop_feedback:64,arm_stow_command:[58,59,109],arm_stow_unstow:168,arm_surface_contact:[69,169],arm_surface_contact_command:69,arm_surface_contact_command_async:69,arm_surface_contact_pb2:69,arm_surface_contact_servic:68,arm_trajectori:170,arm_velocity_command:64,arm_velocity_feedback:64,arm_walk_to_object:171,arm_with_body_follow:172,arm_wrench_command:109,armcartesiancommand:[6,7],armjointmovecommand:6,armless:[64,109],armmov:2,armor:64,armsurfacecontact:[69,164],armsurfacecontactcli:[69,169],armsurfacecontactrequest:69,armsurfacecontactservic:[62,69],around:[2,6,8,10,16,19,20,26,29,31,37,39,40,46,47,55,57,59,61,62,64,71,109,110,128,160,165,171,172,196,197,212,213],arrai:[57,58,64,74,100,138,144,145],arrang:[13,26,64],arriv:[6,59,64,109],arrow:[26,57,64,197],arrow_length:64,arrow_radiu:64,ascend:20,ascii:[2,64],aservic:64,ask:[15,21,38,50,55,57,64,71,153,161],aspect:[18,64,159],assert:[64,88],assertionerror:98,assess:20,assign:[9,22,40,62,64,126,129],assist:[18,43,64,145],associ:[2,16,17,18,21,22,25,26,28,31,32,33,34,36,37,40,41,43,60,62,64,67,76,77,79,80,81,90,92,94,107,108,119,138,139,142,147,175,176,196,197,202,206,207,208,221,223],associated_metadata:81,associated_metadata_proto:41,associatedmetadata:[33,41,80,81],assum:[21,23,26,33,45,51,58,60,61,62,64,85,128,205,206,207],assumpt:7,assur:61,async:[41,62,67,68,69,76,77,79,80,81,82,83,86,87,88,90,92,93,95,97,98,99,101,104,105,107,109,111,115,116,117,118,119,120,121,122,123,124,125,126,130,153,182],async_task:70,asyncgrpctask:70,asynchron:[62,64,70,71,76,80,212],asyncperiodicgrpctask:70,asyncperiodicqueri:70,asynctask:70,ato:64,atomic_file_writ:127,attach:[6,16,18,24,34,36,39,42,43,44,47,62,64,95,108,111,129,207],attack:[23,39,61,62],attempt:[6,8,18,20,23,26,35,39,41,45,50,57,62,64,86,88,92,94,103,105,108,109,126,179,206,207],attempt_counter_state_nam:64,attempted_leas:64,attent:[39,45],attribut:[2,64,100,158],audio:[62,63,114,209],audio_channel_external_m:64,audio_channel_internal_m:64,audio_channel_unknown:64,audiocli:115,audioservic:[62,115],augment:18,auth:[33,39,43,57,61,63,68,103,207],auth_admin_keep:51,auth_async:71,auth_servic:68,auth_with_token:71,auth_with_token_async:71,authclient:[39,71],authent:[23,39,40,41,43,44,55,57,58,62,64,71,78,80,89,108,129,177,206,207,218],authenticate_from_cach:108,authenticate_from_payload_credenti:108,authenticate_with_token:108,authmetadataplugin:74,author:[23,25,26,27,34,36,40,41,53,57,59,64,74,85,89,103,108,112,129,207,208,213,221],authorit:[64,95],authresponseerror:71,authservic:[23,39,61,71],auto:[6,7,29,34,36,55,62,64,116,191],auto_grasp_command:64,auto_scal:[64,116],autofocu:[62,64,121,122],autom:[21,62,64,128,165,207],automat:[6,7,10,14,21,26,28,32,36,39,41,43,50,51,56,57,58,62,64,103,113,126,161,171,191,196,206,207,208,215,224],autonom:[7,9,10,12,13,18,20,21,55,57,64,161,179],autonomi:[3,23,50,159],autonomous_robot_en:209,autoreturn:9,autowalk:[9,10,11,16,17,18,19,20,31,32,41,62,64,175,197,201],aux1:[64,121],aux2:[64,121],avail:[2,6,7,12,18,19,20,25,26,28,32,33,38,39,40,41,43,47,50,51,56,58,60,61,62,64,84,85,94,95,97,101,108,115,116,122,126,165,173,176,181,182,184,186,191,202,206,207,215],available_model:[57,64],averag:[2,48,56,64],avoid:[8,17,19,23,45,47,53,57,62,64,189,204],awai:[6,8,10,17,19,20,29,55,59,62,64,92,107,161,164,210],await:62,awar:[6,62,160,165,171,172],awb:64,awb_mod:123,aws:174,aws_bucket_nam:174,axes:[2,29,37,64,197],axi:[2,4,6,29,37,58,61,62,64,100,197,224],axis_mode_forc:64,axis_mode_posit:64,axis_on_gripper_ewrt_gripp:[58,64],axis_to_align_with_ewrt_fram:[58,64],axis_to_align_with_ewrt_vis:58,azimuth:64,b11205d698e:[60,61],b4ba:43,b_tform_a:100,b_tform_c:100,back:[2,9,10,11,15,26,29,39,41,42,47,55,56,57,58,59,60,62,64,109,121,200,201,205,207,212,224],back_depth:61,back_depth_in_visual_fram:61,back_fisheye_imag:[55,58,61],backend:[33,89],background:[19,23,36,51,61,62,85,94,95,103,108,126,129,206],backlit:19,backspac:26,backward:[6,24,26,29,39,61,64,112,129],bad:[33,39,53,62,64,78,88,95,129],bad_pose_fiduci:64,balanc:[64,109],ball:[37,38,210],ballet:29,ballpark:64,bandwidth:64,banner:6,bar:[26,50,158,206,221],barrier:40,base:[0,3,6,7,11,13,17,19,21,24,29,32,33,34,35,36,38,39,40,41,43,44,50,58,62,64,67,69,70,71,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,132,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,153,154,155,156,157,158,159,172,176,182,189,191,196,212,221],base_data_read:[133,137,149],base_frame_nam:64,base_offset_rt_footprint:64,base_tfrom_sensor:64,basecli:[67,69,71,76,77,79,81,82,83,84,85,86,87,88,90,92,93,95,96,97,98,99,101,102,103,104,105,107,108,109,110,111,115,116,117,118,119,120,121,122,123,124,125,126,130,153,156],basedataread:[133,137,149],basenam:57,bashrc:174,basi:[23,39,53,61,64],basic:[19,24,31,33,43,55,56,61,62,64,129,134,136,159],basic_command_pb2:[21,58],basic_streaming_visu:[62,219],basicconfig:57,bat:60,batch:57,batch_siz:56,batteri:[4,10,13,21,36,40,48,60,61,62,64,105,109,160,161,162,163,164,165,166,167,168,169,170,171,172,198,206,220,224],battery_change_pose_command:109,battery_change_pose_feedback:64,battery_change_pose_request:64,battery_high_miss:21,battery_low_miss:21,battery_st:64,batterymissingerror:105,batteryst:40,battl:39,bblank_spot_v2_0_env:61,bddf:[30,62,63,68,131,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,184],bddf_download:[73,173],bddf_read:173,beacon:18,beagl:196,bearer:33,beat:[2,26,28,29,64],beats_per_circl:[2,29],beats_per_cycl:[2,29],becaus:[6,15,16,17,23,24,38,39,40,56,57,59,60,62,64,107],becom:[10,18,33,48,64,75,108,206],becomeestopcommand:75,been:[2,5,6,10,18,20,21,23,26,33,35,36,39,41,43,58,60,61,62,64,80,88,95,98,107,108,126,134,145,149,174,189,202,206,207,210],befor:[2,10,16,20,21,23,26,28,29,33,36,40,41,43,44,51,55,56,57,58,59,61,62,64,82,85,88,94,105,107,108,109,113,126,138,159,161,162,163,164,166,168,170,196,204,206,207,210,212,215,221],began:29,begin:[2,6,7,10,17,19,21,24,26,29,31,36,41,64,207,212],behalf:[36,62],behav:[10,26,28,35,41,64,175,176,215],behavior:[7,9,10,13,14,18,23,28,29,40,45,53,58,59,64,92,109,129,159,177,180],behavior_fault_id:[64,109],behavior_fault_st:64,behaviorfaulterror:[62,109],behaviorfaultst:40,behaviror:64,behind:[23,36,64,172],being:[2,6,7,8,14,18,23,24,28,32,39,40,41,43,53,57,58,60,61,62,64,80,85,88,92,116,129,191,210,212,215],believ:[20,40,64],belong:[24,64],below:[5,6,10,14,20,21,23,26,32,33,34,36,39,44,47,48,49,50,51,55,56,57,58,59,60,61,62,64,68,152,159,164,173,174,177,189,206,210,212,224],beneath:26,benefit:39,best:[0,3,18,19,41,42,52,54,56,58,62,64,126,160,161,162,163,164,165,166,167,168,169,170,171,172,185,198,212,224],best_estim:64,best_obj:58,best_vision_tform_obj:58,beta29:[60,61],beta:[2,60,61,64],better:[6,10,26,33,47,53,58,59,62,64,196],bettter:64,between:[2,6,8,11,13,16,17,18,20,21,23,26,28,29,33,35,38,39,44,46,47,48,57,58,59,60,61,62,64,70,75,91,92,94,95,100,103,107,126,179,189,196,197,204,210,212,216],beyond:[0,2,3,6,28,34,40,61,62,64],bgr:58,bia:4,bias:38,bias_force_ewrt_bodi:64,bidirect:[39,64],big:[2,19,38,60,64,88],bigger:[58,64],bignum:[2,64],bin:[51,55,56,57,60,200,210,224],binari:[21,24,28,33,62,64,82,98,138,177],bind:[39,55],bind_al:56,bintrai:51,bit:[2,24,27,50,51,64,119,133,139,149,204],bitstatu:[64,117],black:[4,64,210],blackboard:[62,64,158],blackboard_vari:64,bland:19,blank:[62,64,92],blend:45,blink:206,bloat:64,blob:[24,62,64,82,98,177,199,202],blob_data:64,blob_spec:64,block:[10,19,24,28,36,39,41,57,58,62,64,76,78,80,86,95,105,108,109,125,129,132,137,139,144,145,149,176,194,204,206,207,210,212,221],block_entri:64,block_for_trajectory_cmd:[58,59],block_until_arm_arr:[58,59,109],block_until_complet:78,block_writ:[134,139],blocking_captur:[41,94],blocking_capture_funct:94,blocking_dock_robot:86,blocking_go_to_prep_pos:86,blocking_stand:[61,109],blocking_undock:86,blockwrit:[134,139],blue:[16,26,64,197,206,219],bndbox:55,board:[38,44,45,62,117],bob:2,bob_magnitud:[2,29],bodi:[2,18,20,24,26,28,37,40,45,47,49,59,61,62,63,64,91,95,109,172,180,190,196,213,224],body_control:64,body_frame_nam:62,body_height:[61,109],body_hold_param:2,body_movement_statu:64,body_offset_from_hand:64,body_status_mov:64,body_status_settl:64,body_status_unknown:64,body_tform_odom:37,body_tform_payload:64,bodycontrolparam:62,bodyexternalforceparam:109,bodyhold:2,bodymovementstatu:62,boiler:62,boilerpl:[55,57,58],bolt:49,bonu:59,bool:[2,28,64,74,83,85,90,91,94,95,103,108,111,125,126,129,151],bool_valu:64,boolvalu:[2,64],boot:[21,37,64],bootstrap:23,bosdyn:[2,16,23,24,27,33,35,39,41,53,55,57,58,60,61,64,67,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,133,134,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,153,154,155,156,157,158,160,161,162,163,164,166,167,168,169,170,172,173,176,177,189,191,193,194,196,197,202,203,204,206,207,208,219,221],bosdyn_sdk_robot:[20,94],bosdynclientbblank02:61,bosdyngraphnavloc:62,bosdyngraphnavst:[21,62],bosdynnavigateto:21,bosdynpowerrequest:21,bosdynrobotcommand:21,bosdynrobotst:21,boston:[0,1,18,19,24,39,41,42,44,45,46,50,60,61,62,63,64,66,68,76,95,114,131,132,135,159,173,182,206,215,221],bostondynam:[0,3,42,47,50,52,54,60,62,64,71],both:[2,6,7,13,15,18,20,23,24,26,27,29,31,34,36,40,41,43,48,57,59,61,62,64,74,88,162,174,196,204,206,211,221],boto3:174,bottom:[26,50,64],bottom_land:64,bound:[2,28,31,38,40,56,57,58,59,62,189,202,212,223],boundari:[10,26],bounding_box:64,bourn:173,bourre:2,bourree_param:2,box:[26,28,31,38,40,55,56,57,58,59,60,62,64,189,202,210,212,223],bpm:[26,28],brace:64,branch:[13,64],break_on_success:126,breakag:64,bridg:[9,39,57,63,64,68,181],bright:[19,62,64,118,211],bring:[6,26,59,64],broad:[24,39],broadcast:[39,90,206],broken:[26,62],browser:[55,56],brute:[19,62],bucket:[59,62,174],bucket_nam:174,buffer:[21,30,32,41,60,61,63,64,68,75,77,83,113,173,189,210],bug:[39,108],buggi:39,build:[4,17,18,21,33,38,39,53,58,59,62,64,93,104,109,126,175,176,189,204,206,210,221],build_body_external_forc:109,build_execute_choreography_request:67,build_image_request:93,build_inform:64,build_miss:200,build_on_command:[62,109],build_pc_request:104,build_rout:92,build_synchro_command:[59,109],builder:62,built:[3,18,23,33,38,39,44,59,60,64,189,210],bulk:48,bumper:26,bunch:[57,59],bundl:16,bus:48,butt:[2,29],butt_circle_param:2,buttcircl:2,button:[26,32,44,48,60,62,64,88,160,161,162,163,164,165,166,167,168,169,170,171,172,188,198,201,206,220,224],bypass:[10,176,204,206,208,221],bytes_data:41,bytes_delet:64,bytes_per_point:64,bytesio:[57,61],bytestr:[2,64],cabl:[16,197],cach:[16,18,23,40,64,68,92,108],cache_directori:127,cad:46,cadenc:[2,95],cal:[64,125],calcuat:64,calcul:[23,26,37,64],calibr:[7,33,58,62,125],call:[6,13,16,17,18,19,21,32,38,39,41,43,53,55,56,57,58,59,60,61,62,64,70,76,80,85,86,89,94,95,98,101,102,103,104,105,108,109,113,115,116,117,118,119,120,121,122,123,124,125,126,138,141,147,151,176,182,196,197,202,206,207,215],call_async:76,callabl:[74,95,112],callback:[9,22,43,57,62,64,76,95,194],caller:[39,64,76,221],cam:[3,21,31,34,40,41,44,63,64,68,115,116,117,118,119,120,121,122,123,124,183,186,191,206,215],came:24,camera1:221,camera2:221,camera:[6,7,8,23,26,32,36,37,38,40,55,57,58,62,63,94,116,119,125,189,191,198,201,202,205,210,211,212,215,221],camera_calibration_command:125,camera_calibration_command_async:125,camera_calibration_feedback:125,camera_calibration_feedback_async:125,camera_interfac:94,camera_interface_object:41,camera_model:[58,64],camera_result:64,camera_sourc:64,camerabaseimageservic:[41,94],cameracalibrationcalibrationerror:125,cameracalibrationcommand:64,cameracalibrationfeedback:64,cameracalibrationinternalerror:125,cameracalibrationpowererror:125,cameracalibrationresponseerror:125,cameracalibrationrobotcommanderror:125,cameracalibrationtargetnotcenterederror:125,cameracalibrationtimedouterror:125,cameracalibrationusercancelederror:125,camerainterfac:94,can:[0,2,3,6,7,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,31,32,33,34,35,36,37,38,39,40,41,43,44,45,46,47,48,49,50,51,53,54,55,56,57,58,59,61,64,71,76,77,79,80,85,89,91,92,93,94,95,100,103,107,108,109,110,113,121,125,129,139,147,153,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,183,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,219,220,221,222,223],can_power_command_request_off_robot:64,cancel:[26,32,37,41,62,64,76,77,78,79,80,88,89,92,109,125,215],cancel_acquisit:[77,79],cancel_acquisition_async:[77,79],cancel_acquisition_request:78,cancel_check:[41,80],cancel_interv:80,cancelacquisit:[32,64,80,215],cancelacquisitionrequest:80,cancelacquisitionrespons:80,cancellationfailederror:[77,79],candid:120,cannot:[6,20,26,28,40,41,57,64,71,76,92,93,94,103,104,105,107,112,155,176,190,204,206,215,221],cant:64,cap:[47,48,207],cap_v4l:221,capab:62,capabl:[0,6,8,18,21,34,38,40,45,55,61,62,64,75,77,79,80,176,215,221],capac:[4,8,47],capacit:48,capit:[206,207],capsul:64,captil:50,caption:[57,58],captur:[13,16,20,22,30,31,33,37,38,40,55,57,58,62,64,75,77,79,80,81,94,191,198,206,210,212,221],capture_func:94,capture_imag:55,capture_param:64,capture_period:94,capture_period_sec:94,capture_tim:94,captureactionid:[78,79,81],captureparamet:94,car:20,care:[6,35,41,45,56,58,59,60,61,62,64,164,210],carefulli:[20,36,64],carpet:8,carri:[6,8,12,20,21,22,43,47,58,59,62,64],carry_cmd:58,cartesian:64,cartesian_veloc:64,cast:[56,64],cat:[33,44,50],catalina:54,catalog:12,categori:[26,28,36],category_anim:2,category_arm:2,category_bodi:2,category_dynam:2,category_index:57,category_kneel:2,category_step:2,category_transit:2,category_unknown:2,caught:[41,45],caus:[6,10,20,28,36,39,40,41,48,56,61,62,89,95,109,121,160,165,171,172,179,206],cause_fal:64,cause_hardwar:64,cause_lease_timeout:[62,64],cause_unknown:64,caution:[29,64],caviti:60,cell:[39,40,64],cell_format:64,cell_format_float32:64,cell_format_float64:64,cell_format_int16:64,cell_format_int8:64,cell_format_uint16:64,cell_format_uint8:64,cell_format_unknown:64,cell_siz:64,cell_value_offset:64,cell_value_scal:64,celsiu:64,center:[2,6,25,26,27,29,37,47,49,51,58,61,64],center_point:64,center_pt:64,center_px_i:58,center_px_x:58,centerlin:29,centerpoint:2,cert:[61,74,112,120],cert_resource_glob:112,certain:[2,10,19,21,26,28,36,40,45,51,62,64,159,195],certif:[39,64,89,112],cfg:164,chain:[17,39,64],chalk:[6,7,62],challeng:[35,64,88],chanc:[62,64],chang:[0,10,13,15,17,18,20,23,26,29,37,39,40,43,44,50,51,56,57,58,59,60,61,64,85,88,100,116,123,130,164,174,176,188,189,204,206,210,220,221,222,224],changeset:[61,64],changeset_d:[61,64],channel:[24,31,33,39,41,61,62,64,68,73,76,82,89,98,108,112,132,135,137,138,139,147,148],channel_nam:[41,64,146,147,148],channelcredenti:74,chaotic:29,charact:[21,53,62,78,207,221],charg:[4,13,14,21,40,48,59,61,62,64],charge_percentag:64,charger:[4,62],chart:56,chassi:49,cheap:24,check:[13,14,15,18,21,23,26,28,35,36,39,40,41,44,51,56,57,60,61,62,63,64,67,68,77,79,80,86,88,91,95,96,105,108,111,126,129,159,176,200,206,215,221],check_in:88,check_in_async:88,check_in_at_level:88,check_in_at_level_async:88,checkin:[61,64],checklist:[56,57],checkpoint:[56,57],checkpoint_dir:56,checksum:[24,64,133,136,149],checksum_non:64,checksum_num_byt:64,checksum_typ:64,checksum_type_non:64,checksum_type_sha1:64,checksum_type_unknown:64,checksumerror:136,chees:221,chicken:[2,29],chicken_head_param:2,child:[21,37,64,91,158],child_frame_nam:91,child_to_parent_edge_map:[37,64,109],childframeintre:91,children:[21,64,158],chmod:[27,44],choic:[6,64],choos:[15,17,26,33,38,41,51,62,64,93,189],choosen:2,choreograph:[2,25,28,187,217],choreographi:[2,3,27,52,60,65],choreography_nam:67,choreography_seq:67,choreography_sequence_nam:2,choreography_starting_slic:[2,67],choreographycli:67,choreographysequ:67,choreographyservic:67,choroeograph:27,chosen:[39,41,64,92,109],chunk:[63,64],circl:[2,26,29,61,160],circle_region:107,circuit:[48,61],circuitri:48,circular:[64,107],circumst:[14,64],ckpt:56,cladd:210,claim:[64,95],clamp:[8,48,64],clap:[2,28,29],clap_dist:[2,29],clap_param:2,classif:62,classnam:53,claw:64,claw_gripper_close_command:109,claw_gripper_command:64,claw_gripper_feedback:64,claw_gripper_open_angle_command:109,claw_gripper_open_command:109,claw_gripper_open_fraction_command:[59,109],clean:[14,50,62,78,129],clean_filenam:78,cleanli:[41,80,129],cleanup:[80,129],cleanup_request:80,clear:[11,14,17,36,40,41,62,64,85,90,92,94,109,117,127,179,196,208],clear_all_payload_fault:[64,90],clear_all_service_fault:[64,90],clear_behavior_fault:109,clear_behavior_fault_async:109,clear_bit_ev:117,clear_bit_events_async:117,clear_fault:94,clear_graph:92,clear_graph_async:92,clear_service_fault:90,clear_service_fault_async:90,clearabl:[36,40,64],clearanc:[8,165,171,172],clearbehaviorfault:64,clearbitev:64,clearer:62,clearfailederror:127,cleargraph:[17,64],clearservicefault:64,clearservicefaultrespons:90,cli:[44,129,174,206,214],cli_auth:129,cli_login_prompt:129,click:[7,26,27,44,50,55,62,165,171,204,221],client:[0,3,9,12,13,15,16,17,19,20,21,22,23,25,27,28,32,33,34,35,36,37,39,40,41,43,51,52,53,54,55,57,58,59,60,61,64,65,67,69,70,71,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,100,102,103,104,105,106,107,108,109,110,111,112,113,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,160,161,162,163,164,166,167,168,169,170,172,175,176,177,178,179,182,185,189,191,193,194,196,203,204,206,208,213,218,219,221],client_error:64,client_nam:[61,64,95],client_name_func:106,client_name_prefix:112,client_rx:64,client_start_tim:67,client_tx:64,clientcancelledoperationerror:89,clientcontext:[80,94],cliff:[62,64],climb:[47,64,107],clock:[2,23,39,41,53,61,64,73,75,92,94,95,98,108,126,151,177,216],clock_identifi:[64,86,126],clock_skew:[64,126],clockwis:[2,29,64,164],clone:[26,60,189],close:[2,6,15,17,19,20,26,56,57,59,62,64,98,134,138,179,189],closer:[29,64],closest:[26,100,160,161,162,163,164,165,166,167,168,169,170,171,172,198],closest_yaw_only_quaternion:100,cloud:[3,16,20,37,39,63,64,68,107,176,183,197,202],cloud_upload:174,cloudi:64,clutch:64,cmake:189,cmakelist:189,cmd:61,cmd_durat:92,cmd_id:[58,59,109],cmd_respons:58,cnn:202,coast:64,cockpit:[42,51],coco:[210,212],code:[0,4,6,9,12,15,19,21,24,34,36,39,40,41,43,44,53,55,57,58,59,60,61,66,68,73,75,76,82,95,96,97,102,103,104,114,131,132,151,152,153,159,174,182,189,191,194,196,210,221],code_internal_server_error:64,code_invalid_request:[57,64],code_ok:[61,64],code_unspecifi:64,codec:[62,221],coefficeint:64,coeffici:[62,64,107],coher:64,col:[58,64,94],collaps:61,collect:[9,18,19,22,23,32,34,36,53,55,59,62,64,76,80,93,94,104,176,197,210,212,215],collector:48,collid:6,collis:64,color:[16,26,28,40,58,62,116,197,209,210,219],color_gray2bgr:58,color_gray2rgb:57,colormap:[62,116],colormap_greyscal:64,colormap_jet:64,colormap_unknown:64,column:[23,31,41,60,64],com:[0,3,42,47,50,51,52,54,60,61,62,64,71,189,202,210],com_height:[2,29],com_pos_rt_payload:64,combin:[2,5,6,20,21,23,26,28,29,32,33,39,40,43,44,47,48,59,61,62,109,207,224],combo:[39,71,103],come:[2,10,29,35,39,44,47,51,57,59,60,61,62,64,109,119],comm:[10,15,21,33,61,62,64,105,179,182,204],comma:212,command:[3,5,6,7,10,14,15,17,21,22,23,26,27,29,32,36,38,41,45,50,51,53,55,58,59,63,67,68,69,73,86,87,88,92,99,105,108,125,158,159,160,164,165,167,168,170,171,173,174,175,176,177,180,181,182,189,191,192,193,195,197,198,199,200,204,205,206,207,208,209,210,211,212,214,215,216,217,218,219,221,224],command_abort:64,command_cancel:64,command_cli:[58,59,61,105,109],command_dict:75,command_id:[62,64,86,92],command_lin:[75,209],command_revert_c:64,command_start:64,command_status_nam:64,command_unknown:64,commandexpirederror:92,commandfailederror:[86,109],commandfeedbackrequest:40,commandinprogresserror:105,commandlinetool:189,commandrespons:40,commandtimedouterror:[62,105,108,109],commenc:61,comment:[39,59,62,64,75,82,83,98,108,177,199],commerci:18,commit:[62,64],common:[2,6,18,39,40,41,51,53,55,56,57,60,61,62,64,67,68,69,71,77,79,81,82,83,84,85,86,87,88,89,90,92,93,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,115,116,117,118,119,120,121,122,123,124,125,126,129,130,132,135,151,153,156,210,212,221,224],common_header_error:76,common_lease_error:76,commonerror:[39,57,129],commonli:[13,21,37,40,60,108,221],comms_out:175,comms_stat:64,comms_test:175,commsstat:40,commun:[0,3,10,21,25,34,35,39,40,41,43,44,47,50,52,57,60,61,64,74,77,79,80,82,83,84,85,87,88,90,92,93,94,95,97,98,101,102,103,104,105,108,109,111,112,126,130,153,175,191,204,206,210,212,215,221,224],compact:[39,61],compar:[13,17,19,58,62,95,110],compare_eq:64,compare_g:64,compare_gt:64,compare_l:[21,64],compare_lt:64,compare_n:64,compare_unknown:64,compareresult:95,comparison:[21,64,95],compart:[160,161,162,163,164,165,166,167,168,169,170,171,172,198],compat:[23,39,46,50,60,61,62,64,74,112,129],compens:62,compil:[21,32,34,53,56,64,129,153,155,189,207,214],compilationerror:153,compileerror:155,complet:[2,4,8,13,14,15,19,21,23,28,29,32,37,39,41,43,44,48,53,56,58,59,61,62,64,70,78,80,86,89,92,94,95,101,107,125,159,187,196,206,215,221],complete_after_acquir:64,complete_after_sav:64,complete_unknown:64,completion_behavior:64,complex:[9,42,64,159,162],compli:[6,34,64],compliant:[6,64],complic:[21,76],compliment:35,compon:[9,34,42,43,44,45,48,61,62,64,183,215],compos:[13,28,40,109],composit:64,compositor:[62,63,114,209],compositor_pb2:116,compositorcli:116,compositorservic:[62,116],compress:[40,123,221],compress_live_point_cloud:64,compress_point_cloud:64,compression_gzip:64,compression_non:64,compression_unknown:64,compression_zstd:64,compris:[32,62,64],comput:[9,16,18,19,20,23,24,26,31,37,39,40,41,55,57,58,59,60,63,64,68,75,94,100,109,133,136,175,176,181,189,191,204,206,207,216,221],computation:[38,53],compute_stand_location_and_yaw:[58,59],computer_serial_numb:[23,61,64],con:60,concaten:64,concatent:64,concatin:64,concav:64,concept:[0,5,13,30,53,54,59,61,159,207],conceptu:[0,53,62],conclud:22,conclus:16,concurr:57,condens:50,condit:[17,18,21,26,35,61,92],cone:14,conf:58,conf_msg:58,confid:[15,55,57,58,64,202,210],confidence_dogtoi:58,confidence_person:59,config:[43,56,57,64,86,88,102,103,193],configmismatcherror:88,configrang:86,configur:[6,14,19,29,32,39,40,41,42,50,51,60,62,64,75,88,94,111,112,120,125,164,174,175,176,185,189,191,204,207,208,210,211,212,221,224],confirm:[35,204,206],conflict:44,conform:45,confus:62,congest:[62,64],congratul:[59,60],conjunct:47,connect:[3,13,14,17,18,20,23,27,36,39,42,43,44,45,46,48,50,51,52,55,57,60,61,62,64,74,89,92,94,108,120,183,191,196,197,204,207,210,212,215,221,224],connector:[14,39,45,48],consecut:[23,29,196],consequenti:64,consid:[2,6,19,20,26,36,40,45,47,48,53,57,60,64,76,92,164,210,212],consider:[39,47],consist:[0,13,16,18,20,21,23,24,25,26,28,41,53,60,64,70,197,219],consol:[23,43,44,50,64,103,129,207],const_proto:158,constant:[28,36,57,58,62,64,109,136,152],constantli:[210,212],constantresult:21,constantvalu:158,constrain:[6,15,18,64],constraint:[2,7,58,64,92,107],constraintfaulterror:92,construct:[18,26,44,59,64,91,107,109],constructor:[41,62,95],consum:[36,64,206],contact:[5,6,7,15,26,40,45,46,47,50,60,62,63,68,71,180],contact_lost:64,contact_mad:64,contact_unknown:64,contain:[2,10,13,16,17,18,20,21,24,26,28,31,32,33,35,37,40,41,42,48,53,55,60,61,62,64,67,74,76,77,79,80,92,94,97,101,106,108,109,112,127,135,139,142,175,176,177,196,202,206,207,210,221],content:[24,41,51,56,60,64,138,177],content_typ:[64,138],context:[18,28,33,43,57,64,80,94],continu:[6,8,10,13,15,18,40,41,44,48,57,58,59,61,62,64,92,94,95,189,190,201,206,207,219],contralater:2,contrast:37,control:[0,2,3,5,6,7,9,10,13,14,17,18,21,22,28,29,32,39,40,45,51,52,54,58,59,60,61,64,71,75,86,95,109,116,159,160,162,165,175,180,187,188,189,201,211],controlcent:51,controls_arm:2,controls_bodi:2,controls_gripp:2,controls_leg:2,conveni:[26,56,57,60,64,108,111,129],convent:[59,62,132,135],convers:[57,77,126,151],convert:[23,27,28,37,39,41,56,57,58,59,62,64,67,76,78,91,100,108,109,126,130,151,158,177,219],convert_timestamp_from_local_to_robot:151,convert_to_tensor:57,convolut:56,coord:[62,64],coordiant:64,coordiat:64,coordin:[7,12,16,31,32,37,38,40,57,58,61,62,64,95,100,109,116,186,189,197,219],copi:[2,44,51,55,56,57,59,64,69,80,87,99,109,189,207,210],copyfrom:[21,41,58,59],copyright:[60,61],core:[3,23,42,43,44,53,60,61,62,64,65,175,176,204,206,210,221],corner:[16,19,37,45,46,50,58,64],correct:[2,7,23,28,59,60,62,64,109,130,176,204,206,207,215,221],correctli:[19,36,40,44,46,55,58,60,61,62,64,92,189,206,207,215,218,221,223],correl:[2,20,58,219],correspond:[2,21,26,28,32,33,34,38,40,44,61,62,64,92,117,138,139,158,191,197],corridor:[18,20],cost:[62,64],could:[13,15,23,24,26,28,29,37,39,40,53,56,57,59,60,62,64,82,83,89,91,94,95,98,107,109,112,130,153,155,158,196,207,215,221,223],couldn:130,couldnotcreatewaypointerror:107,count:64,countabl:64,countdown:26,counter:[55,64],counterclockwis:[64,164,191],coupl:[41,50,62,64,167,206,221],cours:35,cov_rzrz:64,cov_xi:64,cov_xx:64,cov_xz:64,cov_yi:64,cov_yx:64,cov_yz:64,cov_zi:64,cov_zx:64,cov_zz:64,covari:[62,64],cover:[0,20,23,45,50,54,56,57,60,61,64,109,159,180,187],cpp:221,cpu:[24,48,56,57,64,119],cpython:189,crash:[36,62,64,121,208],crawl:[2,64,224],crawl_param:2,creat:[3,4,11,13,17,18,20,23,24,26,27,28,29,32,34,37,39,40,43,46,47,53,55,56,57,58,60,62,64,71,74,76,77,78,80,94,95,100,107,108,109,110,112,142,176,185,190,196,200,201,203,204,206,207,210,212,216,218,219,220,224],create_capture_thread:[41,94],create_category_index_from_labelmap:57,create_edg:107,create_edge_async:107,create_insecure_channel:74,create_new:95,create_robot:[55,57,58,61,112],create_secure_channel:74,create_secure_channel_cr:74,create_standard_sdk:[55,57,58,61,112],create_strict_vers:110,create_subleas:95,create_waypoint:107,create_waypoint_async:107,created_edg:64,created_waypoint:64,createedg:[17,64],createwaypoint:[16,17,64],createwaypointrespons:62,creation:[62,64,74,207],creation_func:112,creation_tim:64,cred:74,credenti:[23,27,43,44,50,60,61,62,64,71,74,103,108,202,207,208],credit:[60,61],critic:[23,33,39,64,92],crop:64,cross:[20,21,29,59,64,107],crosslink:45,crtical:64,crtl:50,crude:211,cryptographi:62,csq:217,csv:[64,175,202],ctl:75,ctrl:[26,55,204,213,221],cubic:64,cuda:[55,56,59,202],cuda_visible_devic:56,cudnn:56,cudnn_status_internal_error:56,cumul:20,cup:64,curl:33,current:[2,4,6,10,11,14,15,17,18,20,23,24,26,28,29,33,35,37,40,43,44,47,48,56,58,59,61,62,64,67,75,80,86,88,92,95,100,107,108,109,111,112,115,116,124,125,126,139,149,151,153,164,173,174,175,176,177,190,191,192,193,196,205,206,207,213,215],current_mod:64,current_st:[58,64],current_state_str:58,current_tim:58,curv:62,curvatur:16,custom:[18,20,21,23,26,31,32,34,38,39,50,59,62,64,76,77,129],custom_metadata:31,customiz:26,cut:[24,35,61,62,64,88,108,188,189,211,213],cut_immedi:[61,108],cut_power_timeout:[61,64],cv2:[55,57,58],cvtcolor:[57,58],cycl:[2,21,29,48,61,62,64,91,103,105,121,126,196,206,207],cycle_pow:121,cycle_power_async:121,cyclepow:[62,64],cylind:64,cylindr:[6,64],cylindrical_veloc:64,daemon:51,dag:[64,91],dai:[62,173,176,177],damag:[6,19],damp:64,danc:[2,26,28,29,61,62,67,217],dancemov:2,danger:10,daq:[33,64,77,79],dark:[20,64],darker:20,darwin:189,data:[3,4,13,14,16,17,19,20,21,22,23,34,36,37,39,40,42,43,44,48,55,56,57,58,59,61,63,64,67,68,70,73,75,91,92,93,94,98,101,104,107,108,113,115,119,127,132,134,135,136,139,141,144,145,146,147,148,159,191,196,197,202,206],data_acq_cli:78,data_acquisit:77,data_acquisition_cli:78,data_acquisition_download:176,data_acquisition_exampl:176,data_acquisition_help:78,data_acquisition_pb2:[41,78,80],data_acquisition_plugin:79,data_acquisition_plugin_servic:[41,80,176],data_acquisition_plugin_service_pb2_grpc:80,data_acquisition_stor:81,data_acquisition_store_pb2:78,data_block_s:145,data_buff:[82,98],data_buffer_statu:64,data_buffer_total_byt:64,data_captur:64,data_collect_fn:[41,80],data_error:[64,80],data_id:[31,41,64,80,81],data_id_future_pair:80,data_identifi:79,data_nam:[31,41,64],data_queri:64,data_read:[137,141,142,144,147],data_sav:64,data_servic:83,data_sourc:64,data_store_cli:78,data_timestamp:77,data_writ:[138,143,145,148],dataacquisit:78,dataacquisitioncap:[62,80],dataacquisitioncli:[31,77,78],dataacquisitioncommand:75,dataacquisitionobject:33,dataacquisitionplugincli:79,dataacquisitionpluginservic:[32,41,79,80,176,215],dataacquisitionpluginserviceservic:80,dataacquisitionrequestcommand:75,dataacquisitionresponseerror:77,dataacquisitionservic:[77,79],dataacquisitionservicecommand:75,dataacquisitionstatuscommand:75,dataacquisitionstor:[64,176],dataacquisitionstorecli:[78,80,81],dataacquisitionstorehelp:[41,80],dataacquisitionstoreservic:81,databas:[55,64,119],datablock:64,databuff:[62,64,78],databuffercli:[62,82,113],databuffercommand:75,databufferservic:[62,82],datacaptur:41,datadescriptor:[24,139,149],dataerror:[41,80,136],datafil:[141,144,146,147],dataformaterror:[136,138,145,148],datagram:64,dataid:80,dataidentifi:[41,79,80,81],dataqueri:83,dataqueryparam:[78,81],dataread:[133,136,137,141,142,147],dataservic:83,dataservicecli:83,dataservicecommand:75,dataset:[62,210,212],datastor:78,datatyp:64,datatypedescriptor:[137,147],datawrit:[136,138,139,145],date:[44,60,64,176],date_tim:151,datetim:[64,151],datetimeparseerror:151,david:62,dawn:151,daylight:64,db25:48,dbu:51,ddthh:176,deactiv:[60,210,212,224],dead:[39,57,64],deadlin:[15,61,64,80,126],deadline_exceed:61,debug:[27,33,39,40,41,43,57,60,61,62,64,76,94,129,177,215,223],debug_error_str:61,debug_image_filenam:57,deceler:64,decid:[15,20,38,64],decis:[40,204],declar:[17,20,34,53,64],decod:[41,64,94,112,191],decode_format:94,decode_token:112,decompress:221,decor:76,decreas:[64,212],dedic:[44,48,62],deduploggingmessag:129,deem:129,deep:[38,56,202],deeper:54,def:[20,39,41,55,57,58,59,80,174,206],default_app_token_path:129,default_gatewai:206,default_port:57,default_region:[64,107],default_rpc_timeout:62,default_service_nam:[20,34,36,43,55,57,58,61,67,69,71,77,79,81,82,83,84,85,86,87,88,90,92,93,95,96,97,98,99,101,102,103,104,105,107,109,110,111,115,116,117,118,119,120,121,122,123,124,125,126,130,153,156],default_time_sync_interval_sec:126,defaultdict:76,defin:[2,6,12,13,17,18,20,21,22,23,25,28,29,32,33,34,36,39,50,53,57,58,59,64,80,82,85,103,129,138,174,176,207,215],defineblackboard:21,definit:[0,21,25,29,34,37,39,40,53,60,61,62,64,68,84,85,102,108,152,204,207],degrad:117,degre:[4,8,29,53,58,62,64,122,191],degred:64,del:55,delai:[26,28,39,62,64,210,212],deleg:[32,62,64,127],delet:[33,40,55,56,59,62,64,115,119,127,130,177,209,222],delete_async:119,delete_data_pag:83,delete_data_pages_async:83,delete_sound:115,delete_sound_async:115,deleted_timestamp:64,deletedatapag:64,deletesound:64,deliber:64,deliv:[59,107],demo:[21,62,64,160,165,171,172],demonstr:[11,23,28,29,36,39,41,53,54,56,61,62,160,162,165,167,169,171,175,176,177,178,181,189,191,192,193,194,195,196,198,199,200,203,204,207,208,209,216,218,219,222,223],deni:[40,60,89],denial:64,denot:[37,64],dens:41,depend:[6,10,18,19,20,21,23,24,26,27,29,43,44,53,55,57,60,64,89,109,202,210,212,221],depict:[16,21],deploi:[29,39,43,44,58,60,64,221],deploy:[39,44],deprec:[2,39,64,71,85,98,105,109,112,129],depress:[160,161,162,163,164,165,166,167,168,169,170,171,172,198],depth:[7,8,27,40,55,58,61,62,64,93,94,202],depth_in_visual_fram:[40,62],depth_scal:64,dereferenc:64,deregist:[43,64,88],deregister_async:88,deregisterestopendpoint:64,deregistr:64,deriv:[64,89,95,129],desc_block:134,descend:20,descrb:64,describ:[2,4,13,18,19,21,23,24,26,28,29,30,32,33,37,39,40,44,47,53,56,60,61,62,64,80,100,107,109,119,125,129,133,138,139,151,176,190,198,202,204,206,211,212,214,217,221,223,224],descript:[2,11,14,15,17,21,24,25,26,28,36,40,41,43,48,51,60,61,64,112,151,177,207],descriptor:[24,64,133,139,149,158],descriptor_file_offset:64,descriptor_index:139,descriptorblock:[24,134,149],deseri:[39,64,141,146,147],desert:19,design:[6,19,21,22,24,33,34,36,39,42,46,47,60,109,165,171],desir:[2,6,18,26,28,29,41,43,44,50,51,58,62,64,88,94,109,121,155,196,201,204,206],desktop:[50,51,62],destin:[11,15,20,21,50,62,64,92,158,176,196,215],destination_fil:174,destination_fold:78,destination_typenam:158,destination_waypoint_id:[64,92],destination_waypoint_tform_body_go:[62,64,92],destroi:94,detail:[0,5,6,12,13,16,19,20,28,31,33,34,35,39,41,42,43,46,47,50,51,53,54,56,60,61,62,64,71,80,85,86,92,95,109,116,126,174,185,207],detect:[14,16,18,19,37,38,40,41,44,55,56,57,58,62,64,107,159,176,186,189,195,197,202,206,211,221],detect_fn:57,detected_gaug:64,detection_box:57,detection_class:57,detection_covari:64,detection_covariance_reference_fram:64,detection_scor:57,detector:[38,56,62,64,186,210,212],determin:[13,14,17,18,19,20,23,24,29,32,35,36,37,40,41,43,55,57,62,64,75,76,77,92,95,100,107,129,176,189,204,206,207,210,216,221],dev:[0,3,42,51,52,54,62,221],develop:[0,3,9,11,18,25,33,36,39,40,41,43,44,45,47,50,52,53,54,60,64,95,109,112,159,176,189,215],deviat:17,devic:[6,36,42,45,50,61,62,64,94,117,120,121,205,207,221],device_nam:221,diagnos:[40,50,64],diagon:64,diagram:[3,21,26,32,39,41,58,210,212],dialog:55,diamet:[49,164],diari:33,dict:[77,78,79,91,108,133,138,139,210],dictat:[17,64],dictionari:[37,75,109,112],did:[2,33,56,57,59,61,62,64,88,89,95,101,105,108,198,204],didn:[55,56,58,60],dies:24,diff:60,diff_vec:59,differ:[6,10,15,16,18,19,20,21,23,24,25,26,28,29,32,36,37,39,40,41,43,44,50,51,53,55,56,57,60,61,62,64,67,75,77,79,92,93,94,95,97,104,109,126,167,175,176,180,182,184,186,187,189,190,191,196,197,199,204,206,215,219,221],different_epoch:95,different_resourc:95,differenti:[31,53,62],difficult:[18,20,36,53,55,58,62,221],diffus:45,digest:[24,64],dimens:[37,40,57,62,64,138,145],dimension:64,dir:[23,55,56,57,61,75,176,206,221],dir_hint:109,dir_reg_cli:[34,36,85],direct:[2,3,5,6,7,16,20,23,29,50,53,58,59,62,64,85,107,109,160,176,197,201,202,206,207,220],direction_constraint:[64,107],direction_constraint_forward:64,direction_constraint_no_turn:64,direction_constraint_non:64,direction_constraint_revers:64,direction_constraint_unknown:64,direction_hint:64,directli:[6,15,21,26,27,28,36,39,57,59,60,62,64,79,95,109,204,206],directori:[16,27,31,32,36,38,43,55,56,57,59,60,61,62,63,68,75,90,92,93,94,101,107,108,159,160,161,162,163,164,165,166,167,168,169,170,171,172,175,176,182,191,196,197,198,202,204,205,206,207,208,210,212,214,215,221,224],directory_cli:57,directory_modif:[62,178],directory_nam:[34,36,85,215],directory_registr:[57,85],directory_registration_cli:[57,85],directory_registration_servic:68,directory_servic:68,directory_service_author:108,directory_service_nam:108,directory_to_save_miss:201,directorycli:[57,84],directorycommand:75,directorygetcommand:75,directorylistcommand:75,directorymanag:64,directoryregistercommand:75,directoryregistr:68,directoryregistrationcli:[34,36,57,85],directoryregistrationkeepal:[34,36,85],directoryregistrationresponseerror:85,directoryregistrationservic:[23,61,62,85],directoryresponseerror:84,directoryservic:[23,61,84],directoryunregistercommand:75,dirnam:57,disabl:[6,26,48,55,57,62,64,88,105,109,189,201,206,207],disable_alternate_route_find:64,disable_body_force_limit:64,disable_force_on_contact:64,disable_nearmap_cliff_avoid:64,disable_rpc_log:64,disable_vision_body_obstacle_avoid:64,disable_vision_foot_constraint_avoid:64,disable_vision_foot_obstacle_avoid:64,disable_walk:64,disallow_non_stairs_pitch_limit:64,disallow_stair_track:64,disambigu:[53,64],discharg:[48,64],disconnect:[15,26,62,64,91,196,205],discourag:[60,176,204,206,221],discov:[23,37,39,64],discover:64,discoveri:[23,64],discrep:64,discret:177,discuss:61,disjoint:91,disk:[24,32,33,55,62,64],displac:10,displacedleaseerror:95,displai:[2,7,16,23,28,32,41,43,59,60,61,62,64,116,197,206,208,212,215,218,219,221,223],display_delai:212,display_skip:212,dist_2d:[64,107],distanc:[2,6,8,10,19,29,40,58,59,64,92,107,111,151,219],distance_margin:[58,59],distance_str:151,distant:15,distinct:[21,28,55,64],distinguish:19,distort:[64,210],distortion_:64,distribut:[47,53,61,64,112],disturb:[6,59],diverg:64,divers:[39,55],divid:[26,28],divis:59,divx:221,do_ambiguity_check:[64,92],doc:[53,57,110,160,161,162,163,164,165,166,167,168,169,170,171,172,198,212,224],dock:[9,21,63,68,187],dock_config:64,dock_id:[64,86],dock_my_robot:179,dock_properti:64,dock_stat:64,dock_status_dock:64,dock_status_undock:64,dock_status_unknown:64,dock_typ:64,dock_type_contact_prototyp:64,dock_type_spot_dock:64,dock_type_unknown:64,docker:[42,62],dockerfil:[44,62,176,210,221],dockid:179,docking_command:86,docking_command_async:86,docking_command_feedback:86,docking_command_feedback_async:86,docking_command_id:64,docking_servic:68,docking_station_id:[14,64],dockingcli:86,dockingcomand:14,dockingcommand:[14,64,86],dockingcommandfeedback:[14,64],dockingcommandrequest:86,dockingcommandrespons:86,dockingservic:86,dockstat:86,docstr:76,document:[0,3,16,19,20,23,25,26,27,32,33,34,35,40,41,42,43,44,51,52,53,54,56,59,60,61,64,71,125,176,177,180,182,186,187,188,189,190,196,197,198,202,206,207,208,210,217,221],doe:[6,10,13,18,20,23,24,26,28,29,32,33,35,39,40,41,44,50,57,61,62,64,77,84,85,89,90,95,98,103,109,112,136,202,204,206,210,221],does_dedup_filter_exist:129,doesn:[53,56,58,64,92,108],dof:4,dog:[55,56,57,58,59],dogoi:58,dogtoi:[55,56,57,58,59],doing:[39,56,58,64,126],domain:89,don:[26,51,55,56,57,58,59,60,64,78,107,126,153],done:[17,29,37,55,56,58,59,64,76,92,109,194,206,207],door:[5,6,40,62,63,68,164,180],door_command:64,door_command_id:64,door_pb2:87,door_servic:68,doorclient:87,doorservic:[7,62,87,161],doorwai:[7,19],dot:[10,26,28],doubl:[2,15,26,27,28,56,57,64,108,138,176],doublevalu:[2,64],down:[2,7,10,13,20,26,28,29,32,35,39,40,45,48,50,56,57,58,59,60,61,64,95,109,126,129,162,196,198,204,206,207,211,221,224],download:[11,14,17,18,21,22,24,25,26,27,31,39,41,51,55,56,57,58,59,60,62,64,68,78,92,184,196,206,215],download_bddf:173,download_data:73,download_data_rest:78,download_edge_snapshot:92,download_graph:92,download_graph_async:92,download_imag:[64,92],download_started_timestamp:64,download_waypoint_snapshot:92,downloaded_fil:31,downloaded_graph:196,downloadedgesnapshot:[16,17,64],downloadgraph:[16,17,64],downloadsnapshot:16,downloadwaypoint:64,downloadwaypointsnapshot:[16,17,64],dozen:64,drag:[8,26,55,64],drain:206,dramat:[20,29],drastic:17,draw:[6,7,28,57,58,59,62,64,212],drawabl:64,drawable_properti:64,drawn:[26,40,58,64,164],drift:23,drive:[10,14,26,32,38,42,55,57,61,62,64,201],driven:[18,26,196],driver:[50,56,62,64,202,206,224],drop:[10,26,40,55,58,59,64,206,221],drop_position_rt_vis:59,dtype:[55,57,58],dual:45,due:[39,53,60,61,62,64,89,95,105,206,210,212,214],duplic:[44,109,129,138],durabl:45,durat:[2,26,28,29,32,48,53,59,64,95,126,151,177],duration_str:151,duration_to_second:151,dure:[2,6,9,12,13,15,16,17,18,21,22,26,28,31,32,39,40,41,43,48,56,57,60,61,62,64,80,84,85,90,94,103,105,125,155,175,215],dust:[8,45],duty_cycl:[2,29],dwell:2,dylib:189,dynam:[0,1,18,19,21,24,37,39,41,42,44,45,46,50,60,61,62,63,64,66,68,76,95,114,131,132,135,159,173,182,206,215,221],each:[2,4,6,12,13,16,18,19,20,21,23,24,25,26,27,28,29,31,32,33,34,35,36,37,40,41,43,47,48,49,53,55,57,58,61,62,64,68,80,93,94,97,104,109,113,118,138,139,150,152,159,160,174,176,177,182,191,194,196,197,201,206,207,210,212,215,219,221],eap:[50,62],earli:[41,62,64,80],earlier:[51,61,62],eas:[29,36,207],easi:[36,55,56,59,64,129,188],easier:[24,34,50,53,64,109,159],easiest:19,easili:[26,27,38,41,43,44,45,50,59,60,64,110,202],easing_cubic_in_out:2,easing_cubic_input:2,easing_cubic_output:2,easing_exponential_in_out:2,easing_exponential_input:2,easing_exponential_output:2,easing_linear:2,easing_quadratic_in_out:2,easing_quadratic_input:2,easing_quadratic_output:2,easing_unknown:2,ecdsa:62,echo:[39,64],ecryptf:64,edg:[11,13,15,16,17,26,37,49,62,91,92,107,196,197],edge_env:107,edge_environ:[64,107],edge_id:[64,92],edge_id_list:92,edge_map:64,edge_snapshot:[16,92,196,197],edge_snapshot_:196,edge_snapshot_id:[64,92],edge_sourc:64,edge_source_alternate_route_find:64,edge_source_fiducial_loop_closur:64,edge_source_odometri:64,edge_source_small_loop_closur:64,edge_source_unknown:64,edgeexistserror:107,edgemissingtransformerror:107,edgesnapshot:92,edit:[26,51,60,151,206],effect:[2,20,26,29,43,53,62,64],effector:[6,7,62,64,167,169],effici:[2,24,33,55,64],effort:36,eight:[2,64],either:[2,11,13,21,24,26,28,31,33,36,37,40,41,43,51,57,60,62,64,89,92,95,100,101,107,109,126,138,139,151,189,196,221],el0:64,el1:64,elaps:[2,64],elbow_0:2,elbow_1:2,elect:13,electr:[0,14,42,45],element:[2,31,37,62,64,76,138,200],elev:47,elif:[55,57],ellipt:62,els:[26,57,58,59,60,62,64,92,224],elsewher:[56,155],email:60,embed:33,emerg:[26,60,61,88,210],emit:98,empti:[6,18,20,26,28,31,36,56,58,61,86,91,107,112,126,138],empty_region:107,enab:57,enabl:[10,26,28,33,34,36,40,41,43,48,50,55,57,58,59,60,61,62,64,85,105,109,116,119,160,161,162,163,164,165,166,167,168,169,170,171,172,189,198,206,207,208,221],enable_bit:64,enable_congestion_control:64,enable_debug:119,enable_debug_async:119,enable_grated_floor:64,enable_humid:64,enable_shock:64,enable_system_stat:64,enable_temperatur:64,enablecongestioncontrol:64,enabledebug:64,encapsul:37,encod:[2,18,23,24,40,62,82,138,221],encoding_paramet:64,encoding_raw:64,encoding_rl:64,encoding_unknown:64,encoding_xyz_32f:64,encoding_xyz_4sc:64,encoding_xyz_5sc:64,encount:[16,17,36,41,64,89,155],encourag:[15,40,61,62],encrypt:[39,64],end:[2,6,7,10,14,15,21,22,24,26,28,29,32,33,39,41,50,51,54,55,58,59,62,64,73,78,92,109,125,126,133,134,139,149,167,169,177,206,210],end_datetim:126,end_nsec:[73,126],end_tim:[58,59,64,73,86],end_time_sec:[58,59,78,109],end_timestamp:64,endian:[24,64],ending_veloc:64,endmag:24,endpoint:[8,31,32,35,39,40,44,57,60,62,67,88,92,108,109,126,129,130,153,179,196,204,207,213,215],endpoint_ip:[204,207],endpoint_port:207,endpointmismatcherror:88,endpointunknownerror:88,endstop:[64,125],energet:26,energi:48,enforc:[26,28,64],engag:[179,188,205],engin:[9,36],enhanc:[40,50],enough:[14,19,20,47,53,57,64,95,100,165,171,172,210],enp2s0:50,ensur:[26,28,29,36,41,43,45,46,48,49,50,56,60,62,64,94,95,98,103,108,165,171,172,176,202,205,206,215,221],ensure_channel:108,ensure_cli:[20,34,36,43,55,57,58,61,62,108],ensure_secure_channel:108,enter:[20,26,28,29,31,51,55,56,57,60,64],enterpris:62,entir:[6,28,29,31,34,41,58,61,64,75],entranc:28,entrance_st:2,entri:[21,31,53,57,62,64,75,84,85,103,139],entry_slic:[2,29],enumer:[33,88,95,154],env:107,environ:[6,10,17,19,20,33,39,40,44,45,50,51,55,56,59,61,62,64,107,160,161,162,163,164,165,166,167,168,169,170,171,172,174,198,210,212,218,224],environment:[46,174],environmn:64,eof:149,eoferror:149,ephemer:[62,129],epoch:[33,61,64,95,108,126,138,145,148,151,173,177],equal:[10,21,51,64],equip:[19,20],equival:64,eras:[64,92],err_str:57,error:[2,7,28,33,36,40,53,57,60,61,62,70,71,73,74,76,77,79,80,82,83,84,85,86,88,89,90,91,92,93,94,95,97,98,101,103,104,105,107,108,109,112,125,126,127,129,136,153,154,155,156,158,177,204,215,221],error_camera_timeout:64,error_clutch_slip:64,error_cod:[129,157],error_constructor:76,error_data:[64,80],error_edge_id:64,error_endstop_timeout:64,error_existing_edg:64,error_factori:76,error_failed_stand:64,error_fgkc_failur:64,error_from_respons:76,error_ground_check:64,error_init_imu_check:64,error_init_not_sit:64,error_loadcell_timeout:64,error_mass_discrep:64,error_messag:[64,67,71,76,77,84,85,88,89,90,92,93,94,95,101,103,104,105,107,109,125,153,156,157],error_msg:[80,129],error_no_result:64,error_non:64,error_obstruct:64,error_pair:76,error_power_off_failur:64,error_power_on_failur:64,error_report:64,error_revert_failur:64,error_unexpected_power_chang:64,error_unknown:64,error_waypoint_id:64,error_waypoint_localized_id:64,error_zero_out_of_rang:64,escap:26,especi:[6,29,45,58,64],essenti:[6,59,60,159],essid:64,est:57,establish:[3,19,23,34,40,48,64,74,89,120,126,156,206,216],establish_sess:156,establish_session_async:156,establish_timesync:126,establishsess:[21,64,204],estaimted_payload:64,estim:[7,16,18,20,23,37,40,63,64,108,109,126,151,161,197,219],estimated_end_effector_force_in_hand:64,estimated_payload:64,estimated_runtim:64,estop:[23,35,58,60,61,62,63,68,75,105,108,196,210,217],estop_cli:[61,88],estop_cut_power_timeout:88,estop_endpoint:61,estop_gui:[60,188,196],estop_keep_al:61,estop_level_cut:[61,64,88,188],estop_level_non:[61,64,88],estop_level_settle_then_cut:[64,88],estop_level_unknown:[64,88],estop_nogui:[35,60,188],estop_pb2:88,estop_servic:68,estop_st:64,estop_timeout:[61,88],estopcheckin:64,estopcheckinrequest:35,estopcheckinrespons:35,estopcli:88,estopconfig:88,estopconfigrequest:64,estopendpoint:[61,88],estopkeepal:[61,62,88],estopnogui:62,estoppederror:105,estopresponseerror:88,estopservic:[23,61,62,88],estopst:[40,105],estopsystemstatu:88,etc:[16,24,33,39,44,50,51,60,61,62,64,75,108,120,138,197,204],eth0_a_n:48,eth0_a_p:48,eth0_b_n:48,eth0_b_p:48,eth0_c_n:48,eth0_c_p:48,eth0_d_n:48,eth0_d_p:48,ethernet:[3,4,8,14,39,43,48,50,64,76],euler:[2,59,64,100,150],eulerzxi:[61,109,150],eval:57,eval_input_read:56,evalu:[55,56,64],even:[6,10,15,36,39,40,44,47,58,60,61,64,120,200,215],event:[2,16,18,28,32,48,62,75,80,82,83,117,119,177],event_comment_request:64,event_level_min:33,events_com:64,eventscommentsspec:83,eventu:[59,64,215],ever:[41,64,103],everi:[16,19,20,21,26,29,31,34,38,56,61,64,85,109],everyth:[55,57,80,207],ewrt:64,exact:[26,35,37,64],exactli:[2,15,26,29,35,59,62,64,95,189],exagger:29,examin:76,exampl:[0,2,3,4,6,7,9,10,12,13,15,17,18,19,20,23,24,26,27,28,31,32,35,37,39,40,44,45,50,51,53,54,55,56,58,60,61,62,64,80,82,88,92,93,95,100,109,158,201,202,206,207,210,212,214,215,224],exce:[33,48,64,107],exceed:[33,58,61,64,126],excel:[56,60],except:[39,41,53,58,60,61,64,67,68,71,76,77,80,82,83,84,85,88,90,91,92,93,95,98,101,103,104,105,107,108,109,112,125,126,127,136,151,152,153,156,158],exchang:[64,120],excit:59,exclud:64,exclus:[2,29,33,64],excut:2,exe:[23,27,51,60,218],exec:51,execstart:[51,57],execstartpr:51,execstop:[51,57],exectut:64,execut:[2,6,10,13,14,21,25,26,27,28,36,40,44,51,53,60,61,62,64,67,70,76,86,153,158,162,163,166,168,170,174,189,215],execute_choreographi:67,execute_choreography_async:67,executechoreographi:[2,28],executechoreographyrequest:67,executor:80,exercis:[11,18,41,62,213],exhibit:109,exist:[6,11,23,26,28,32,35,37,39,40,53,55,57,60,62,64,77,84,85,88,90,91,92,94,95,103,107,108,112,119,120,129,130,133,149,176,196,204,206,217,221],exists_in_directori:64,exit:[26,41,50,51,55,57,58,59,60,62,64,80,95,126,129,201,207,210,212,213,220,221,224],exit_slic:[2,29],exit_st:2,expand:[33,34,59],expans:3,expect:[20,33,34,35,40,41,44,53,60,61,62,64,86,91,92,94,95,103,105,138,139,176,210,211,212,215,224],expens:[38,53],experi:[26,61,62,64,160,161,162,163,164,165,166,167,168,169,170,171,172,198,212,221,224],experienc:[45,64,89,92,95],experiment:[60,61,158],expir:[23,39,62,64,71,92,108,112],expiredapplicationtokenerror:71,expirederror:109,expiri:86,explain:[0,18,24,61,62,64],explan:[56,207],explic:50,explicit:64,explicitli:[10,13,14,16,18,31,53,60,62,108],explor:[39,60],exporter_main_v2:57,expos:[14,23,40,43,44,45,61,108,109,196],exposur:[41,62,64,94],exposure_dur:64,express:[6,13,21,24,37,40,58,59,61,64,100,112,150],express_se2_velocity_in_new_fram:91,express_se3_velocity_in_new_fram:91,extend:[2,26,28,29,32,41,44,47,64],extens:[4,8,12,51,64,80,81,93],extent:[47,64],extern:[4,18,24,33,36,40,41,43,58,60,62,64,101,108,109,176,179,191,211,213,221,224],external_force_ind:[64,109],external_force_non:[64,109],external_force_overrid:64,external_force_param:[64,109],external_force_use_estim:[64,109],external_force_use_overrid:[64,109],external_m:[64,121],externalservererror:101,externalservicenotfounderror:101,extra:[6,14,26,39,56,62,64,71,80,102,103,104,116,215],extra_payload:64,extract:[24,56,59,100,173],extraordinari:64,extrem:[26,40,64],extrins:[62,64],f0e835c2:209,fab:58,face:[20,29,59,62,64,161,164],facil:61,facilit:64,factor:64,fail:[13,21,23,33,39,40,41,44,56,57,58,60,61,62,64,78,87,88,92,93,101,104,107,108,109,112,125,127,151,153,155,179,206,207,208,214,215],failed_nod:64,failed_st:58,failur:[21,28,33,36,41,60,62,64,80,89,94,125,129,154,158,206,215],fake:40,fall:[6,20,28,29,33,45,47,48,59,62,64],fallback_log:98,fallen:[26,64,109],fals:[2,21,29,41,43,58,61,64,73,75,80,82,83,85,88,90,92,94,95,105,107,108,109,111,119,125,126,129,138,148,149,158,189,210],falseclass:[2,64],famili:64,familiar:206,fan:60,faq:27,far:[2,10,12,17,20,29,57,60,62,64,92,109],farm:32,farther:80,fashion:12,fast:[2,18,28,29,41,164],faster:[28,29,64,202,210,212],faster_rcnn_inception_v2_coco:[202,210,212],fastest:[29,50],fastest_ti:29,fault:[3,40,41,61,63,64,68,75,85,92,94,105,109,183,185,206,215],fault_client:94,fault_id:64,fault_nam:64,faultclient:[90,94],faultcommand:75,faultederror:105,faultresponseerror:90,faultservic:90,faultshowcommand:75,faultstat:[64,105],faultwatchcommand:75,favor:[2,62],fcbwxjy6mmjqg:196,fddb:24,feasibl:[28,64],featur:[6,9,16,17,18,19,32,44,50,61,64,92,96,159,197,206],feature_cod:64,feature_en:64,feature_list:96,feature_quality_toler:64,featuredeserterror:92,featureless:[19,64],feed:[62,64],feedback:[14,17,41,53,58,59,61,77,79,87,92,99,109,125],feedback_request:58,feedback_resp:58,feedback_status_nam:64,feel:[56,64],feet:[2,29,61,62,64,109,213],fell:15,femal:48,fetch:[41,54,62],fetch_serv:59,few:[18,20,23,24,36,39,40,53,55,60,61,62,64,212],fewest:15,ffig:196,fiddl:80,fiduci:[6,13,16,18,20,37,40,62,64,92,107,179,186,196,197,205,219,223],fiducial_001:6,fiducial_filtered_pose_statu:64,fiducial_follow:[62,189],fiducial_init:[64,92],fiducial_init_nearest:[19,64],fiducial_init_nearest_at_target:[19,64],fiducial_init_no_fiduci:[19,64],fiducial_init_specif:[19,64,92],fiducial_init_unknown:64,fiducial_pose_statu:64,fiducial_pose_uncertain:64,fiducial_too_old:64,fiducialinit:19,fiducialposeerror:107,fiduicial_init_specif:62,field:[2,4,19,20,26,28,31,32,33,35,36,37,38,39,40,41,43,44,50,53,57,58,61,62,64,76,81,85,89,92,93,94,100,104,106,119,151,155,189,206,207,221,223],field_desc:158,field_desc_to_pb_typ:158,field_nam:155,field_typ:155,fieldmask:64,figur:[29,53,56],figure8:29,figure8_param:2,file:[2,13,16,22,27,30,32,33,39,43,44,51,53,55,56,57,58,59,60,61,62,64,67,75,78,80,81,93,108,132,133,134,135,136,137,138,140,141,142,143,144,145,147,148,149,165,171,173,174,175,176,178,179,188,189,191,192,195,198,199,200,202,204,205,206,207,209,210,211,212,213,214,215,216,217,218,221,222,223],file_cont:64,file_descriptor:[64,133],file_extens:[64,80,81],file_index:[64,133,138,139],file_lin:61,file_nam:[64,67],file_offset:[24,64,139],file_path:67,fileformatdescriptor:24,fileformatvers:133,fileindex:[24,133,138,139],filenam:[31,55,62,64,73,78,93,127,174],filepath:[93,189,196,217,221],filesystem:[64,119,127],fill:[6,35,36,37,41,57,59,62,64,86,92,93,104,111,119,126,207,215],filli:196,filter:[33,37,40,58,62,64,129,130,176,195],find:[10,23,24,26,55,56,57,58,59,60,62,64,75,89,91,92,93,104,130,155,159,176],find_center_px:58,findstr:60,fine_tune_checkpoint:56,fine_tune_checkpoint_typ:56,finger:[7,8,64],fingertip:[58,64],finish:[14,29,55,58,59,64,80,201,204],finish_block:145,firewal:[39,57,176,202,204,206,207,221],firmwar:35,first:[2,6,20,21,23,24,26,28,29,31,33,35,39,40,43,44,51,53,55,57,59,60,61,62,64,79,88,103,109,111,112,126,173,179,189,190,202,207,210,215,221],first_checkin:88,fishey:[37,61,62,64,161,206,210,214],fit:[8,26,56,62,64],five:[37,212],fix:[2,18,28,29,37,39,41,43,59,64,89,94,189,221],fixed32:[2,64],fixed64:[2,64],fixnum:[2,64],flag:[27,61,62,64,221],flashlight:62,flat:[6,19,28,37,64,107,125],flat_bodi:[29,64,100],flat_body_q_hand:59,flat_body_tform_hand:59,flat_ground:[64,107],flatten:[31,100],flatter:53,fledg:60,fleet:[9,22,112],flexibl:[9,24],flexion:4,flight:[2,29],flight_slic:[2,29],flip:[2,109],float32:[2,64],float64:[2,64],float_valu:[21,64],floatvalu:[57,58,64],floor:[28,29,58,62,64,160,161,162,163,164,165,166,167,168,169,170,171,172,198],fluoresc:64,flush:[58,98],fly:55,foam:[45,46],focal:64,focal_length:64,focu:[62,64],focus:[28,41],folder:[32,38,55,56,57,58,78,176,189,196,200,215,224],follow:[2,3,4,6,7,10,11,13,15,16,17,18,19,21,22,24,26,27,28,29,32,33,35,36,37,39,41,43,44,47,50,51,52,53,54,55,56,58,59,60,61,62,64,65,80,82,85,92,100,107,109,111,158,159,164,173,176,180,181,182,183,184,185,186,187,200,201,202,204,206,207,211,212,214,215,218,220,221,224],follow_arm_command:109,follow_arm_feedback:64,follow_arm_request:64,followingrouteerror:107,font_hershey_simplex:[57,58],foo:[53,158],fooouterclass:53,fooproto:53,foot:[2,29,40,62,64,109,213],foot_height_error_from_mean:64,foot_height_result:[62,64],foot_posit:[64,109],foot_position_rt_bodi:64,foot_stat:64,footprint:[2,29,61,62,64,109],footprint_r_bodi:[61,109],footstat:40,footstep:[2,16],for_autonomous_process:64,forc:[7,8,19,28,45,56,62,64,109,169,180],force_i:109,force_remain_near_current_joint_configur:64,force_sigint_captur:129,force_simple_setup:[61,88],force_trajectori:162,force_wrench_control:162,force_x:109,force_z:109,forcefulli:[64,95],fordur:21,forest:64,forev:[36,55,57],forget:[21,51,57,62,64,103],forgotten:[35,103],form:[23,24,29,31,39,53,64,71,82,86,89,91,105,108,197],format:[26,28,30,31,39,40,41,55,56,57,58,60,61,62,73,91,93,94,136,137,141,144,147,149,151,173,176,177,202,210,212,215,221],format_bddf_fil:64,format_jpeg:[41,57,64],format_metr:151,format_raw:[41,57,58,64],format_rl:64,format_unknown:64,former:60,forth:29,forward:[2,6,26,29,37,39,44,50,51,59,61,62,64,79,92,98,109,172,190,201,207],found:[10,19,28,29,33,37,38,40,47,50,56,57,58,59,60,62,64,84,89,92,93,94,101,104,108,115,159,160,161,162,163,164,165,166,167,168,169,170,171,172,174,198,210,212,221,224],foundat:59,four:[2,14,17,26,28,32,64,221],fov:64,fpn:56,fraction:[2,29,64,151],fragil:6,frame1:64,frame1_nam:64,frame2:64,frame2_nam:64,frame:[2,3,16,18,20,24,29,40,45,58,59,64,68,100,108,109,159,180,187,189,197,219,223],frame_a:91,frame_b:91,frame_bodi:62,frame_c:91,frame_help:[37,58,59,62,91],frame_ko:62,frame_nam:[58,59,64,91,109],frame_name_camera:64,frame_name_dock:64,frame_name_draw:64,frame_name_fiduci:64,frame_name_fiducial_filt:64,frame_name_image_coordin:[58,64],frame_name_image_sensor:[58,64],frame_name_local_grid_data:[37,64],frame_name_sensor:64,frame_name_tform_box:64,frame_trajectory_command:[62,190],frame_tree_edg:37,frame_tree_snapshot:[91,109],frame_vo:62,frametre:91,frametreesnapshot:[37,62,91,108],frametyp:62,framework:[7,28,44],free:[56,64,207],freedesktop:51,freedom:[4,8,26],freez:[21,64,109],freeze_command:109,freeze_feedback:64,freeze_request:64,frequenc:[48,105,108,109],frequent:[50,64],fresh:[23,128],fri:57,friction:[62,64,107],fridai:33,fridg:64,friend:60,friendli:[23,64,75],from:[0,2,4,6,7,8,10,11,13,14,15,17,18,19,20,21,23,24,25,26,27,28,29,31,32,33,34,36,37,39,40,41,42,43,44,45,47,48,49,50,51,53,55,56,57,58,59,61,62,64,67,69,71,73,75,76,77,78,79,80,81,82,83,85,86,87,88,89,90,91,92,93,94,95,97,98,99,100,102,104,105,107,108,109,110,112,119,120,125,126,127,129,130,133,134,137,139,140,141,144,146,147,149,151,153,158,159,161,164,165,171,173,175,177,179,189,191,196,197,198,200,202,204,205,206,207,210,211,212,213,214,215,217,221],from_ident:100,from_ko_tform_stair:64,from_matrix:[59,100],from_nsec:33,from_obj:[20,59,100],from_pitch:100,from_proto:88,from_rol:100,from_se2:100,from_sec:33,from_t_to:107,from_tform_to:[64,107],from_timestamp:64,from_vector:100,from_waypoint:[64,107],from_waypoint_id:107,from_yaw:100,frombuff:[55,57],fromstr:58,front:[2,4,6,14,29,37,43,47,55,56,58,59,62,64,109,161,186,191,210,211],front_up_param:2,frontleft:[37,64],frontleft_depth:[61,191],frontleft_depth_in_visual_fram:61,frontleft_fisheye_imag:[55,58,61,191,202],frontright:64,frontright_depth:61,frontright_depth_in_visual_fram:61,frontright_fisheye_imag:[55,58,61],frontup:2,frozen:[61,202],frozen_inference_graph:202,full:[2,6,8,16,17,29,32,36,37,39,40,41,48,55,62,63,64,69,75,77,79,82,87,99,101,108,124,125,145,148,196],full_body_command:109,full_body_feedback:64,fullbodycommand:6,fulli:[6,13,28,29,40,41,46,48,49,55,59,62,64,85,105,129,176,189,204,206,207,221],fullstatecommand:75,fun:[59,60],func:76,fundament:159,furnitur:19,further:[10,24,26,28,45,59,62,64,71,110,217],furthermor:62,furthest:64,fut:61,futur:[2,15,23,33,50,57,61,62,64,71,76,80,92,109,110],futurewrapp:[70,76],g00:164,g01:164,g02:164,g03:164,g3doc:202,gaffer:164,gain:[41,61,62,64,94,115,207],gait:[2,29,64],game:21,gamepad:26,gamma:4,gas:[18,41],gasket:46,gatewai:[43,50,64],gather:[18,43,64],gaug:64,gaze:[62,64,109,180],gaze_to_target_rotation_measur:64,gazecommand:6,gazing_at_target:64,gcc:[60,61],gcode:[6,7,62,180],gcp:62,gener:[6,8,11,17,21,23,24,26,29,31,32,33,34,37,39,41,43,44,53,58,61,62,64,67,71,74,77,79,84,85,88,90,91,92,93,95,100,103,104,105,107,108,109,112,125,126,127,129,153,176,197,201,206,207,210,212,215,221],generate_channel_opt:74,generate_client_nam:112,generate_route_param:92,generate_tfrecord:56,generate_travel_param:92,generatetreeerror:91,gentli:[40,59,61],geometr:[20,37,61,64],geometri:[3,16,40,45,47,61,62,63,91,109,131,180,187,190],geometry_pb2:[58,59,100],get:[11,13,14,17,19,20,21,23,26,33,37,40,43,45,54,55,56,57,58,59,61,62,64,67,70,75,76,77,78,79,80,84,86,87,91,94,95,96,97,98,100,103,104,107,108,109,110,111,112,116,119,120,123,126,130,133,138,139,140,146,153,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,177,181,182,183,186,189,191,196,198,207,209,210,212,224],get_a_tform_b:[58,59,62,91],get_bit_statu:117,get_bit_status_async:117,get_blob_spec:[64,83],get_bounding_box_imag:58,get_bytes_field_whitelist:129,get_cached_robot_id:108,get_cached_usernam:108,get_challeng:88,get_closest_se2_transform:100,get_closest_se3_transform:100,get_colormap:209,get_config:88,get_config_async:88,get_data_buffer_statu:83,get_data_buffer_status_async:83,get_data_index:83,get_data_index_async:83,get_data_pag:83,get_data_pages_async:83,get_docking_config:86,get_docking_config_async:86,get_docking_st:86,get_docking_state_async:86,get_entri:84,get_entry_async:84,get_events_com:83,get_events_comments_async:83,get_feature_en:96,get_frame_nam:91,get_frame_tree_snapshot:[108,109],get_graphnav_origin:20,get_hardware_config_with_link_info:111,get_ice_configur:120,get_ice_configuration_async:120,get_id:[61,108,110],get_id_async:[61,110],get_imag:[41,93,191],get_image_and_timestamp:94,get_image_async:93,get_image_from_sourc:[55,61,93],get_image_from_sources_async:93,get_info:153,get_info_async:153,get_ir_colormap:116,get_ir_colormap_async:116,get_latest_captured_imag:94,get_leas:95,get_lease_st:95,get_led_bright:118,get_led_brightness_async:118,get_license_info:96,get_local_grid:97,get_local_grid_typ:97,get_local_grid_types_async:97,get_local_grids_async:97,get_localization_st:[20,92],get_localization_state_async:92,get_logg:129,get_messag:[140,141,146,147],get_miss:153,get_mission_async:153,get_mission_st:[62,192],get_new_estim:126,get_obj_and_img:[58,59],get_odom_tform_bodi:91,get_payload:62,get_payload_auth_token:103,get_point_cloud:104,get_point_cloud_async:104,get_point_cloud_from_sourc:104,get_point_cloud_from_sources_async:104,get_posit:209,get_power_statu:121,get_power_status_async:121,get_proto_read:[141,142],get_ptz_posit:122,get_ptz_position_async:122,get_ptz_veloc:122,get_ptz_velocity_async:122,get_record_statu:107,get_record_status_async:107,get_request_id:77,get_request_st:80,get_robot_clock_skew:126,get_robot_hardware_configur:111,get_robot_hardware_configuration_async:111,get_robot_joint_model_async:111,get_robot_link_model:111,get_robot_link_model_async:111,get_robot_metr:111,get_robot_metrics_async:111,get_robot_st:[4,59,61,111,193],get_robot_state_async:[61,62,111,194],get_robot_time_convert:126,get_screen:116,get_screen_async:116,get_se2_a_tform_b:91,get_self_ip:[57,62,76],get_service_info:[77,79],get_service_info_async:[77,79],get_software_vers:124,get_software_version_async:124,get_software_version_ful:124,get_software_version_full_async:124,get_stat:153,get_state_async:153,get_statu:[61,77,79,88,119],get_status_async:[77,79,88,119],get_status_proto:80,get_stream_param:123,get_stream_params_async:123,get_temperatur:117,get_temperature_async:117,get_time_sync_upd:126,get_time_sync_update_async:126,get_transl:100,get_value_from_constant_value_messag:158,get_value_from_value_messag:158,get_visible_camera:116,get_visible_cameras_async:116,get_vision_tform_bodi:91,get_volum:[115,209],get_volume_async:115,get_walking_param:[58,59],get_world_object:[62,195],getaudiocapturechannel:64,getaudiocapturegain:64,getauthtoken:[39,64],getauthtokenrequest:[39,62],getauthtokenrespons:[39,60,62],getbitstatu:64,getdatabuffercommentscommand:75,getdatabuffereventscommand:75,getdatabuffereventscommentscommand:75,getdatabufferstatu:64,getdatabufferstatuscommand:75,getdataindex:64,getdatapag:64,getdockingconfig:[14,64],getdockingst:[14,64],getestopconfig:64,getestopsystemstatu:64,geteventscom:64,getfeatureen:[62,64],geticeconfigur:64,getimag:[32,40,41,62,64,94,206,221],getimagecommand:75,getimagerequest:94,getinfo:[21,64],getircolormap:[62,64],getledbright:64,getlicenseinfo:64,getlocalgrid:[40,64],getlocalgridscommand:75,getlocalgridtyp:[40,64],getlocalizationst:[15,17,20,64],getmapstatu:64,getmiss:[21,64],getobjectlistrespons:64,getpayloadauthtoken:[43,64],getpointcloud:64,getposit:64,getpowerstatu:64,getptzposit:64,getptzveloc:64,getrecordstatu:[17,64],getrobothardwareconfigur:[40,64],getrobotid:[61,64],getrobotlinkmodel:64,getrobotmetr:[40,64],getrobotst:[40,64],getscreen:64,getservic:64,getserviceentri:[43,64],getserviceinfo:[32,41,64,80],getserviceinforequest:80,getserviceinforespons:[77,79,80],getsoftwarevers:64,getsoftwareversionrespons:124,getstat:[21,64],getstatu:[32,41,62,64,78,80,215],getstatusrequest:80,getstatusrespons:[41,80],getstreamparam:64,gettemperatur:64,getvisiblecamera:64,getvolum:64,gif:[2,25,29,59],gigabit:8,gigabyt:33,git:[60,189],github:[0,189,202,210],give:[26,28,43,50,53,61,64,109],given:[13,15,17,19,21,29,34,37,40,47,62,64,74,75,76,82,83,88,92,93,94,95,98,100,104,107,108,126,133,137,139,140,142,144,147,149,151,158,173,207,209],glide:60,glob:[61,112],global:[16,18,20,62,64,197,221],glossi:19,glove:59,glue:23,gmt:33,gn_origin_tform_bodi:20,gnd:48,gnome:51,gnu:[50,189],goal:[15,26,55,56,58,59,62,64,109,189,190],goal_head:[58,59,109],goal_heading_rt_bodi:109,goal_i:[58,59,109],goal_se2:109,goal_x:[58,59,109],goal_x_rt_bodi:109,goal_y_rt_bodi:109,goe:[21,29,54,56,64,206],going:[6,21,26,28,29,56,58,59,61,62,64,98],gone:[62,64],good:[18,28,36,38,39,55,56,57,58,62,64,95,109,172,210],googl:[2,39,50,53,57,58,62,64,65,77,80,82,98,126,151,174],google_application_credenti:174,googleapi:61,got:[20,29,57,58,59,109],gpe:[37,64],gps_metadata_plugin_servic:176,gpu:[40,55,56,64,119,202,210],grab:[7,50,64,67,75,95,109,130,153,161],gracefulli:39,grade:64,grai:58,grant:[43,95,207],graph:[13,16,17,18,21,23,37,56,61,63,68,107,159,181,196,197,202],graph_nav:[19,23,61,68,92,107],graph_nav_cli:20,graph_nav_command_lin:[11,16,62,196],graph_nav_servic:68,graph_nav_view_map:62,graphic:[51,56,60,214],graphnav:[9,11,12,13,39,40,62,64,68,92,107,181],graphnavcli:[20,92],graphnavrecord:17,graphnavrecordingservic:[12,13,21,62,107],graphnavrecordingservicecli:107,graphnavservic:[12,23,61,62,92],graphnavserviceresponseerror:92,grasp:[6,7,13,58,59,62,64,171,180],grasp_don:58,grasp_palm_to_fingertip:[58,64],grasp_param:[58,64],grasp_params_frame_nam:[58,64],grasp_planning_solut:64,grasp_position_constraint_fixed_at_user_posit:64,grasp_position_constraint_norm:64,grasp_position_constraint_unknown:64,grasp_request:58,graspabl:58,grate:[62,64,107],grated_floor:[62,64,107],grav_aligned_body_frame_nam:59,graviti:[2,37,59,61,64,91,100,109],grayscal:[40,57,58,62],great:[26,55],greater:[2,51,64,210,212],greatest:24,green:[58,60,64,188,197,206],grei:[26,28],grep:60,greyscal:[40,64],grid:[37,62,63,64,68,75,219],gripper:[2,6,7,26,28,58,59,63,64,164,180],gripper_command:[6,59,109],gripper_command_feedback:64,gripper_nearest_object:64,gripper_open_percentag:64,gripper_param:2,gripper_q:109,grippercommand:6,grossli:56,ground:[2,7,19,29,37,40,48,55,62,64,107,125,169],ground_mu_hint:[62,64,107],group:[2,27,31,36,64,77,78,159],group_nam:[31,32,64,77,78],group_name_format:64,grow:62,grpc:[23,24,33,41,52,53,57,61,62,64,65,70,73,74,76,80,85,89,94,95,102,103,104,105,108,113,126,127,129,132,135,182,204,206,207,210,212,215,221],grpc_messag:61,grpc_proto_read:140,grpc_python_out:207,grpc_reader:[141,142],grpc_servic:[33,73],grpc_service_read:142,grpc_service_writ:143,grpc_statu:61,grpc_tool:207,grpcio:207,grpcprotoread:[140,141,142],grpcreader:141,grpcrequest:135,grpcrespons:135,grpcserieswrit:143,grpcserviceread:142,grpcservicerunn:[34,129],grpcservicewrit:143,guarante:[6,23,35,37,40,64],guard:59,guess:[13,19,23,64,92],gui:[2,26,43,56,60,62,210],guid:[0,25,29,36,41,43,44,52,53,54,55,56,60,61,62,64,90,103,108,159,160,161,162,163,164,165,166,167,168,169,170,171,172,176,185,189,196,198,206,207,208,221],guidanc:45,guidelin:[42,43,47],gxp:39,gyro:64,gzip:64,had:[59,64,80,92,109],half:[47,64],halfwai:58,hallwai:[19,20],halt:6,hamburg:[57,59,204],hammi:196,hand:[8,29,55,58,59,64,172,211],hand_distance_to_goal_measur:64,hand_ewrt_flat_bodi:59,hand_position_at_go:64,hand_roll_at_go:64,hand_roll_to_target_roll_measur:64,handi:50,handl:[7,24,26,33,34,40,41,47,48,51,53,56,64,76,80,95,102,104,105,108,109,112,127,129,161,202,207],handle_common_header_error:76,handle_lease_use_result_error:76,handle_respons:76,handle_response_stream:76,handle_stal:64,handle_stale_fail:64,handle_stale_read_anywai:64,handle_stale_run_until_fresh:64,handle_stale_unknown:64,handle_typ:64,handle_type_fixed_grasp:64,handle_type_knob:64,handle_type_lev:64,handle_type_unknown:64,handle_unset_status_error:76,handler:[64,76,98,129],handlestal:62,handoff:39,handshak:39,happen:[35,39,54,56,61,62,64,85,92,93,95,104],happend:64,happi:58,hard:[6,45,59,64,174,179],hardcod:[43,64],hardwar:[15,23,36,40,41,45,48,61,62,64,75,109,111,176,193,201,212,215],hardware_configur:64,hardwareconfigur:[62,111],hardwareconfigurationcommand:75,has:[0,2,4,6,7,8,10,13,14,15,16,17,18,20,21,23,24,25,26,28,33,35,36,37,38,39,40,41,43,45,50,51,55,56,57,58,59,60,61,62,64,71,80,85,88,92,93,94,95,98,104,105,107,108,109,111,125,126,134,136,158,164,173,177,188,189,202,205,206,207,210,215,221,224],has_arm:[62,108,111],has_data:64,has_data_error:80,has_established_time_sync:126,hash:[24,64,139],hasn:58,have:[2,5,6,7,15,16,18,19,20,21,23,24,26,28,29,31,33,34,35,36,37,38,39,40,41,43,46,47,50,53,55,56,57,58,59,60,61,62,64,80,86,89,94,95,98,100,109,126,130,149,160,172,173,174,175,179,189,190,195,196,197,198,201,204,205,206,207,209,210,211,213,215,216,217,218,221,222,223],haven:[64,145],head:[2,6,29,55,56,57,58,109],header:[2,24,33,39,41,53,57,61,62,63,76,89,106,113,125,129,134,138,157],header_pb2:[57,129],heading_rt_vis:[58,59],headless:56,health:[0,40,43,63,114,119,125],health_scor:64,healthclient:117,healthservic:[62,117],hear:[10,153],heart:80,heartbeat:[35,36,64,85],heavi:[6,42,45,47,64],height:[2,4,8,16,19,29,40,55,61,62,64,100,109,197,219,224],height_z:100,held:[6,64,108],hello:[62,159,182,204],hello_spot:[55,60,198],hello_world_mission_servic:204,helloworldservic:204,help:[11,13,14,17,18,19,23,24,26,28,35,38,41,45,50,54,55,56,57,58,60,61,62,64,86,88,109,126,160,161,162,163,164,165,166,167,168,169,170,171,172,173,176,198,206,212,215,224],helper:[28,34,37,41,58,59,61,68,76,80,85,86,93,103,104,107,108,109,113,176,204,206,207,215,221],henc:[45,61,89],here:[2,21,24,26,28,34,36,38,40,47,50,55,56,57,58,60,61,64,80,109,159,160,161,162,163,164,165,166,167,168,169,170,171,172,174,198,202,206,210,212,221],hhmmss:177,hide:[39,95],hierarchi:[13,21,28],high:[2,3,6,7,14,15,21,23,25,26,28,29,33,39,40,48,57,61,62,64,206],higher:[3,7,16,23,29,39,40,44,45,64,88,197,210,221],highest:[58,60,64,210],highest_conf:58,highli:60,highlight:[28,159],hind:[2,4,29],hing:[7,62,64,161],hinge_sid:64,hinge_side_left:64,hinge_side_right:64,hinge_side_unknown:64,hint:[16,60,62,64,107,109],hint_ambl:64,hint_auto:64,hint_auto_ambl:64,hint_auto_trot:64,hint_crawl:64,hint_hop:64,hint_jog:64,hint_left:64,hint_right:64,hint_speed_select_ambl:64,hint_speed_select_crawl:64,hint_speed_select_trot:64,hint_trot:64,hint_unknown:64,hip:[37,47,62,64],hip_i:[2,29],hip_range_of_motion_result:64,hip_x:[2,29],histor:[40,62,64],histori:64,historical_fault:64,history_lower_tick_bound:64,history_past_tick:64,history_upper_tick_bound:64,hit:[26,64],hntr5:49,hold:[2,6,8,29,33,56,59,64,75,109,189,212],hold_zero_ax:[2,29],holder:[61,64,95],holding_toi:58,hole:64,home:[21,51,55,56,60],hop:[2,64,224],hop_param:2,hope:59,hopefulli:10,horizon:[64,202],horizont:[2,28,29,37,38,58,62,116],host:[0,21,23,34,35,39,43,44,50,55,57,62,64,73,74,85,108,129,176,202,204,206,207,208,210,221],host_computer_ip:[44,176,206,221],host_ip:[36,64,85],host_payload_guid:64,hostcomputeripcommand:75,hostnam:[26,27,33,44,55,57,58,60,73,78,129,175,176,202,204,206,210,212,221,224],hotkei:26,hour:[23,33,43,62,173,176,177,207],how:[2,7,11,13,18,20,21,23,26,27,28,29,30,32,34,35,36,37,38,39,40,41,44,50,51,53,54,55,56,57,58,59,60,62,64,80,86,92,109,112,125,133,138,139,159,160,162,165,167,169,171,173,174,175,176,177,178,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,198,200,203,204,205,206,207,208,209,210,214,216,217,218,219,221,222],howev:[6,8,9,14,16,18,19,20,23,26,27,28,37,38,39,41,43,45,50,53,60,62,64,80,91,95,103,176,189,191,207,221],html:64,http2:64,http:[23,33,44,50,51,56,60,61,62,64,78,138,189,202,206,210],huge:56,human:[8,19,32,40,41,64,75,107,116,207,210],humid:[64,119],hundr:62,hunk:82,hxa:64,hya:64,hybrid:[6,64,162],icd:0,ice_serv:120,icon:[2,26,51,206],icp:[64,92],icp_vari:64,id_client:61,id_end:64,id_service_nam:108,id_start:64,idea:[7,40,62],ideal:[53,76],ident:[6,23,29,51,64,100,215],identif:[64,88,202],identifi:[23,28,31,32,36,38,40,41,58,61,62,64,79,80,81,86,92,108,112,126,136,139,207,223],identifier_hash:64,identifit:64,identify_object:202,idr:64,idr_interv:123,idrinterv:64,ids:[64,78,92,107,196],iff:64,ignor:[2,6,29,35,36,59,62,64,89,109,116],ignore_final_yaw:64,ikqi6hgq:196,ill:[64,89],illumin:62,illustr:[37,45],imag:[2,3,4,7,16,19,21,28,31,32,34,37,38,50,55,56,57,58,59,62,63,68,75,77,79,80,81,92,101,119,123,161,164,165,171,175,176,182,183,186,189,198,202,209,210,212,220],image_captur:[55,64,80],image_cli:[55,61],image_data:[41,94],image_decod:[41,94],image_decode_with_error_check:94,image_dir:56,image_format:[41,64,93,94],image_fram:94,image_ful:58,image_height:57,image_nam:[44,94],image_param:64,image_pb2:[41,57,58,93,94],image_properti:[57,58,64],image_proto:[41,94],image_request:[64,93],image_respons:[55,58,61,64,93],image_saved_path:55,image_servic:[68,94],image_service_help:[41,94],image_service_pb2_grpc:[34,94],image_service_test:[41,215],image_sourc:[55,58,64,93,94],image_source_and_servic:[58,64],image_source_nam:[64,93,94],image_tensor:57,image_typ:[64,94],image_type_depth:64,image_type_unknown:64,image_type_visu:64,image_uuid:209,image_width:57,imageacquistioncap:41,imagecaptur:[80,81,93],imagecapturethread:94,imagecli:[55,61,93],imagecommand:75,imagedataerror:93,imagedemo:174,imageformat:41,imagerequest:93,imagerespons:[93,214],imageresponseerror:93,imageri:[16,197],imageservic:[23,32,41,61,62,93,215],imageserviceservic:94,imagesourc:[62,93,94,215],imagesourceandservic:58,imagetyp:[62,94],imdecod:[55,57,58],img:[55,58],immedi:[7,26,29,35,41,47,48,61,62,64,80,88,108,204],immut:64,impact:[39,40,45,47,64],impair:62,imperfect:58,impl:[21,64],implement:[0,3,9,11,17,18,21,23,35,36,38,39,40,41,53,61,62,64,77,79,80,81,88,94,98,158,183,204,207,221],impli:[24,61,62,64],implic:39,imposs:[20,64],improperli:[39,64],improv:[33,36,38,39,40,60,64],imshow:58,imu:[8,64,125],imwrit:[55,57],inabl:39,inaccess:10,inaccessibleparametererror:155,inaccur:18,inact:[57,64,206],inactivethreaderror:126,inadvert:6,incandesc:64,incept:210,inch:164,inclin:64,includ:[0,4,5,8,9,11,12,16,18,20,21,23,24,26,29,31,32,33,34,36,37,39,40,41,42,43,44,45,47,48,50,53,55,56,57,58,60,61,62,64,65,71,74,76,77,78,79,80,87,92,93,95,99,107,109,111,126,129,130,134,159,174,176,177,189,191,197,202,203,206,210,212],include_dedup_filt:129,include_full_lease_info:[64,95],include_statu:158,inclus:64,incom:[57,64,80,109],incompat:[28,60],incompatible_hardware_error:64,incomplet:92,inconsist:19,incorpor:[6,46,62,159],incorrect:[2,26,35,57,60,62,64,67,77,79,88,136,179,207,221],incorrect_challenge_respons:35,incorrectchallengeresponseerror:88,incorrectli:[32,53,62,64,71,80,108],increas:[39,47,58,60,61,62,64,95,210,212],increment:[29,64],inde:204,indent:53,independ:[12,14,27,29,36,39,61,64],index:[14,24,57,62,63,64,82,83,132,133,137,138,140,141,144,146,147,149,177,221],index_data_block:139,index_in_seri:[137,140,141,144,146,147],index_offset:134,indic:[2,13,14,15,28,29,36,39,41,59,61,62,64,71,78,80,92,94,95,107,108,109,118,119,129,153,177,201,202,206],individu:[6,26,28,31,33,35,41,62,64,221],indoor:45,industri:[18,45],ineffici:[2,64],inert:42,inerti:[37,64],inertia:64,inf:58,infer:[19,62,64,202],influenc:[40,64],info:[2,17,33,56,64,75,80,105,110,129,177,204,221],inforamt:64,inform:[0,2,3,4,14,16,18,19,20,21,23,24,26,28,29,31,32,33,34,36,38,39,40,41,42,43,46,50,51,52,53,54,60,61,62,64,77,79,80,84,85,92,94,95,100,107,108,109,112,113,124,126,137,153,159,174,176,177,178,182,188,192,204,206,207,208,215,221,223],infrastructur:[39,53],ing:44,ingress:[4,8,46],inherit:[34,41,62,76,94],init:57,initi:[2,9,13,16,17,20,23,26,29,32,36,37,40,53,55,56,61,62,64,92,94,107,112,122,126,153,160,162,163,165,166,167,168,169,170,171,191,196,198,205,206],initial_guess:[19,64,92],initial_guess_loc:92,initialize_fault:94,initialize_len:[121,122],initialize_lens_async:122,initialize_payload:207,initializelen:[62,64],inject:60,inkscap:164,inner:[21,45],innermost:64,inoper:6,input:[6,7,18,38,39,41,44,48,53,56,57,58,62,64,80,91,100,109,126,151,156,161,176,196,202,206],input_data:[57,58,64],input_path:56,input_tensor:57,input_typ:57,inrush:48,ins:[40,61,88,95],insecur:74,insert:[21,24,26,33,50,64,105,201],insid:[6,13,31,40,47,60,64,202,207],inspect:[16,18,31,64,80,105],instal:[21,26,44,48,50,54,55,56,62,64,75,96,124,160,161,162,163,164,165,166,167,168,169,170,171,172,173,175,176,177,178,179,188,189,191,192,193,194,195,196,197,198,199,200,201,203,204,205,207,208,209,211,213,215,216,217,218,219,220,221,222,223],install_d:[61,64],instanc:[2,33,34,36,40,41,60,62,64,69,74,76,80,85,86,87,90,94,95,99,108,109,126,207],instantan:64,instanti:[108,158],instantli:26,instead:[2,10,20,23,24,26,29,36,41,43,44,50,56,57,59,62,64,72,98,109,121,174,191,204,206],instruct:[26,44,50,51,60,176,189,201,206,210,212,221],insuffici:[39,64],int32:[2,28,57,58,64],int32valu:[2,64],int64:[2,24,64],int64valu:64,int8:64,int_valu:64,integ:[2,24,28,41,64,88,93,138,151,210],integr:[3,8,30,32,38,42,47,50,52,57,215],intellig:109,intend:[22,29,33,64,79,80,88,98,109,189,201,206],intens:[40,64,214,219],intent:[62,121],intenum:[88,154],interact:[7,8,21,23,39,42,44,50,51,60,61,62,64,75,95,120,129,189,209,219],intercept:61,interconnect:45,interest:[6,32,57,64],interf:50,interfac:[0,14,23,32,33,38,39,41,42,44,45,50,53,54,61,62,64,66,68,73,75,76,80,94,109,114,127,131,132,152,188,197,201,206,220],interfer:8,interlock:[14,48],interlockmotor:48,intermedi:[39,64],intermediari:120,intermitt:64,intern:[4,19,20,24,36,39,40,53,62,64,80,88,93,94,95,98,104,125,126],internal_server_error:53,internalservererror:[89,95],internet:[3,39,50],interpol:64,interpret:[7,23,29,38,39,60,61,62,64,138,158,164],interrupt:[15,39,48,64,179,207],intersect:[19,64],intertia:64,interv:[13,26,62,70,85,108,126],interven:20,intervent:6,intranet:3,intrins:[62,64],introduc:[8,64],introduct:[6,159],introductori:[62,167,198],invalid:[2,15,23,39,40,60,62,64,67,71,77,88,89,91,92,93,95,104,109,125,145,153,158],invalid_request:53,invalid_schema_id:64,invalidanswercod:153,invalidapplicationtokenerror:71,invalidapptokenerror:89,invalidargu:[82,83,98],invalidclientcertificateerror:89,invalidconvers:158,invalidedgeerror:92,invalidendpointerror:88,invalididerror:88,invalidleaseerror:95,invalidloginerror:[39,71,108],invalidpayloadcredentialserror:[103,108],invalidquestionid:153,invalidrequest:2,invalidrequesterror:[89,92,108,109],invalidresourceerror:95,invalidsessionid:156,invalidtokenerror:[71,108],invaliduploadedchoreographyerror:67,invers:[37,64,100],invert:[37,64],invoc:[102,103,104],invok:[75,95],involv:[16,48,64,92,200,204],ioerror:112,ion:48,iosdmpfeqvdthzgv:61,ip54:[4,45,46],ip_where_plugin_will_run:176,iperf3:175,ipv4:[50,61,64],ircolormap:116,is_al:[85,95,103,128],is_author:[43,64],is_battery_low:21,is_battery_low_miss:21,is_block:76,is_cancel:80,is_download:64,is_en:[43,64],is_estop:[62,88,108],is_extend:2,is_gravity_aligned_frame_nam:91,is_gripper_holding_item:64,is_lost:64,is_metadata:[64,138,148],is_noncompute_payload:[43,64],is_open:64,is_point_cloud_process:64,is_powered_on:[61,105,108],is_record:64,is_robot_following_hand:64,is_string_identifi:158,is_thread_al:98,is_tim:64,is_valid_proto:95,is_within_threshold:100,isdir:57,isinst:57,isn:[2,14,20,23,56,57,59,64],iso8601:64,isrecordingerror:92,issu:[2,6,7,14,21,23,27,36,39,40,43,50,53,56,57,58,60,61,64,67,69,71,86,87,88,92,93,94,95,99,101,104,105,108,109,125,161,176,190,193,196,206,207,210,215,221,223],issue_acquire_data_request:78,item:[6,7,40,43,56,57,64,165,171],iter:[13,21,58,64,76,80,126,189,210,212],its:[2,6,13,14,15,17,18,19,20,21,23,24,25,26,28,29,31,32,33,34,35,37,40,41,43,44,45,47,53,55,57,58,59,60,61,62,64,80,86,92,95,106,109,140,160,165,171,172,189,190,191,202,204,206,207,211,213,214],itself:[3,6,15,17,23,31,33,34,35,37,39,40,43,45,50,57,61,62,64,85,95,103,108,204,207],jacobian:64,jan:[60,61],java:[2,53,64],java_outer_classnam:53,jaw:[8,64],jet:[64,209],jitter:62,jog:[62,224],join:[39,55,57,64,94],joint:[2,29,37,40,59,62,64,109,125,180],joint_limit:64,joint_stat:[4,64],journalctl:50,joystick:[6,64],jpeg:[40,41,57,62,64,93,94,209],jpg:[31,55,56,57,61,93,209],json:[32,41,64,77,79,174,176,210],jump:[2,50,58],jump_param:2,just:[6,7,14,21,27,56,58,59,60,61,62,64,76,107,112,175,191,196,206],jwt:[23,112],kcapabl:41,keep:[6,8,10,18,20,26,34,36,40,41,44,47,53,56,57,58,59,60,61,62,64,85,88,95,103,125,139,176,210,221],keep_al:[34,36],keep_running_cb:[61,88,95],keepal:36,keepalivestatu:88,kei:[0,23,24,26,39,54,55,56,57,61,62,64,67,76,77,79,81,82,83,86,92,98,105,120,127,130,133,135,136,138,139,145,153,201,204,224],kept:[14,112,129,207],kera:56,kernel:50,key_to_series_identifier_hash:64,keyboard:[50,60,62,207],keyerror:149,keypress:26,keystrok:26,kick:[80,85,103,108,204],kill:[51,61,207],kimagesourc:[58,59],kind:[24,41,62,64,138,139],kinemat:[21,37,40,62,64,109],kinematic_cal_result:64,kinematic_st:[59,64],kinematicst:[40,62],kitchen:64,kna:64,knee:[2,4,19,28,29,37,47],kneel:[2,28],kneel_circle_param:2,kneel_clap:29,kneel_leg_move2_param:2,kneel_leg_move_param:2,kneellegmov:2,kneellegmove2:2,knock:164,knonerror:80,knot:64,know:[2,37,40,43,61,62,64,101,109,221],knowledg:[23,37,40,190],known:[16,17,18,19,23,26,37,40,41,43,64,91,94,95,108,151,196,197,206],ko_tform_bodi:[64,92],kserviceauthor:57,kw_arg:[102,103,104],kwarg:[39,61,67,69,71,76,77,79,81,82,83,84,85,86,87,88,90,92,93,95,96,97,98,99,101,104,105,107,109,110,111,115,116,117,118,119,120,121,122,123,124,125,126,130,153,156,157],lab:19,label:[16,21,26,55,56,57,58,59,64,197,202,207],label_map:[56,57,58,59],label_map_path:56,label_map_util:57,label_path:57,label_prefix:[43,64],labelimg:[55,56],labelprefix:62,labels_fil:57,labels_path:56,lack:[20,210],lag:64,lah:56,laid:[36,207],lamport:95,lan:43,land:[29,50],landing_extent_i:64,landing_extent_x:64,languag:[0,39,53,61],laptop1:31,laptop:[3,27,38,44,204,207,210],larg:[6,16,17,18,20,29,33,36,38,39,40,55,61,62,64,92,164,196],larger:[40,47,64,89,212],laser:41,laser_scan_dens:41,laser_scan_spars:41,last:[2,28,29,40,56,58,59,60,61,64,94,95,107,126,173,210,212,221,224],last_cal_timestamp:64,last_command:64,last_ko_tform_go:64,last_upd:[64,85],lastli:[41,57,64,176,206,215,221],latenc:[6,41,62,64,210,212],later:[9,21,23,29,31,32,33,39,41,50,56,57,62,64,80,98,207],latest:[18,21,44,56,60,62,64,70,94,108,206],latter:60,launch:[51,60,62,128,160,161,162,163,164,165,166,167,168,169,170,171,172,190,198,202,206,217,221],launcher:60,laundri:59,layer:[3,18,23,39,40,45,55,64],layout:[26,62,64,196],lbs:4,ld_library_path:189,lead:[18,50],lead_auto:2,lead_front:2,lead_hind:2,lead_left:2,lead_leg_pair:[2,29],lead_right:2,lead_unknown:2,lean:64,learn:[11,12,55,56,57,58,59,62,64,159,160,161,162,163,164,165,166,167,168,169,170,171,172,181,182,183,198,206,212,224],leas:[2,15,40,58,59,63,67,68,75,76,86,87,89,92,105,107,109,125,153,156,196,201,204,220,224],lease_cli:[58,59,61,95],lease_curr:95,lease_keep_al:61,lease_own:[61,64,95],lease_proto:95,lease_resourc:64,lease_servic:68,lease_st:95,lease_statu:95,lease_use_result:[2,64,95],lease_wallet:95,leasecli:[58,61,95],leasecommand:75,leaseerror:67,leasekeepal:[58,61,95],leaselistcommand:75,leasenotownedbywallet:[62,95],leaseresourc:95,leaseresponseerror:95,leaseretain:64,leaseservic:[61,89,95],leasest:95,leaseuseerror:[87,89,92,95],leaseuseresult:[2,95],leasewallet:[86,95],leasewalletrequestprocessor:95,leasewalletresponseprocessor:95,least:[20,24,26,29,40,41,48,57,64,160,165,171,201,207,215],leav:[10,19,36,60,64,80,86,213],led:[3,62,63,118],left:[2,4,6,26,29,31,36,37,44,49,55,61,64,109,191,197,201,206,211,219,221,224],left_camera_tform_soccer_bal:37,left_depth:61,left_depth_in_visual_fram:61,left_fisheye_imag:[55,58,61],left_fisheye_image_0000:[55,56],left_fisheye_image_0001:56,left_hip_i:[2,29],left_hip_x:[2,29],left_kne:[2,29],left_x:57,leg:[4,26,28,29,37,64],leg_front_left:2,leg_front_right:2,leg_hind_left:2,leg_hind_right:2,leg_no_leg:[2,29],leg_pair_distance_chang:64,leg_pair_result:[62,64],leg_unknown:2,len:[37,56,57,58,64],length:[2,4,28,40,53,59,64,74,112,129,151],less:[21,26,28,29,35,47,59,64,80,105],let:[2,3,6,7,15,26,51,52,53,55,56,57,58,59,61,62,64,120],letter:[19,50,196,206],levata:62,level:[3,6,7,16,19,21,25,28,29,33,35,39,40,53,55,60,61,62,88,98,129,221],level_debug:64,level_error:64,level_high:64,level_info:64,level_low:64,level_medium:64,level_mission_crit:64,level_system_crit:64,level_unknown:64,level_unset:64,level_warn:64,lexicograph:64,lgpl:50,lhs:[21,64],lib:[57,61,189],libapriltag:189,librari:[0,9,23,28,34,35,39,44,53,58,60,61,62,64,65],licens:[25,27,50,60,61,63,68,75,89,107,112,129],license_statu:64,licensecli:[62,96],licensecommand:75,licensed_featur:64,licenseerror:89,licenseservic:[62,96],lidar:[18,19,20,41,43,50,62,64,107],lie:[37,64,107],lies:47,life:[54,62,64],lifecycl:80,lift:[2,6,8,29],liftoff:[2,28,29],liftoff_veloc:29,light:[3,4,19,26,42,55,62,64,114,187,209],lighter:26,lightingcli:118,lightingservic:[62,118],like:[2,4,10,12,14,16,26,28,29,31,36,37,40,43,47,50,53,55,56,57,58,59,60,61,62,64,67,76,77,79,81,82,83,86,89,92,98,105,130,137,138,151,153,196,197,204,215,221],likelihood:[6,39],likewis:[9,33],limb:[2,64],limit:[4,8,10,17,23,28,33,35,39,43,47,48,59,62,73,103,175,189,207],lin_i:100,lin_x:100,lin_z:100,linalg:59,line:[10,22,23,26,27,28,33,38,41,49,51,53,56,57,58,59,60,61,64,73,88,164,165,173,174,177,181,189,191,193,197,201,204,206,207,208,212,214,215,219,221],line_numb:64,linear:[6,21,59,62,64,100],linear_spe:64,linear_veloc:64,linestrip:64,link:[6,10,26,29,34,35,39,40,41,43,44,50,56,74,94,111],link_model:64,link_nam:[64,111],link_status_connect:64,link_status_error:64,link_status_unknown:64,link_to_next:[2,29],lint:80,linux:[26,27,50,60,61,174,176,189,218,221],list:[2,6,14,15,17,21,23,24,26,28,31,32,37,38,40,43,44,50,51,57,60,62,64,67,70,74,75,77,79,80,81,82,83,84,86,91,92,93,94,95,96,97,100,101,102,104,108,109,112,115,116,117,119,122,130,135,138,139,149,155,156,158,165,171,173,175,176,177,178,179,188,191,192,195,196,198,199,200,201,203,204,205,206,207,209,210,211,212,213,215,216,217,218,220,221,222,223],list_all_mov:67,list_all_moves_async:67,list_async:84,list_available_models_command:101,list_available_models_command_async:101,list_available_models_status_external_server_error:64,list_available_models_status_external_service_not_found:64,list_available_models_status_success:64,list_available_models_status_unknown:64,list_camera:[119,209],list_cameras_async:119,list_capture_act:81,list_capture_actions_async:81,list_image_sourc:[61,93],list_image_sources_async:93,list_leas:[61,95],list_leases_async:95,list_logpoint:[119,209],list_payload:[43,102],list_payloads_async:102,list_physical_devic:56,list_point_cloud_sourc:104,list_point_cloud_sources_async:104,list_ptz:122,list_ptz_async:122,list_request:101,list_screen:116,list_screens_async:116,list_servic:108,list_sound:115,list_sounds_async:115,list_stored_data:81,list_stored_data_async:81,list_stored_imag:81,list_stored_images_act:81,list_stored_images_async:81,list_stored_metadata:81,list_stored_metadata_async:81,list_world_object:130,list_world_objects_async:130,listallmov:[2,28],listallmovesrespons:28,listavailablemodel:[38,57,64],listavailablemodelsrequest:[57,101],listavailablemodelsrespons:[57,101],listcamera:64,listcaptureact:64,listdata:64,listen:[39,51,64,176,204,206,221],listimagesourc:[32,40,41,64,94,221],listimagesourcescommand:75,listimagesourcesrequest:94,listleas:[62,64],listleaseresourc:64,listlocalgridtypescommand:75,listlogpoint:64,listpayload:[43,64],listpointcloudsourc:64,listptz:64,listscreen:64,listserviceentri:[23,43,64],listsound:64,liststoreddata:64,liststoredimag:64,liststoredmetadata:64,listworldobject:[40,62,64],lit:[19,206],lithium:48,littl:[24,33,50,57,64],live:[23,34,35,36,40,55,56,62,64,85,95,208,212,219,221],live_data:64,liveness_timeout_sec:[64,85],load:[2,4,13,19,21,44,56,57,59,62,64,67,112,137,153,196,202,209,217],load_app_token:112,load_cell_result:64,load_choreography_sequence_from_binary_fil:67,load_choreography_sequence_from_txt_fil:67,load_miss:153,load_mission_async:153,load_robot_cert:112,load_sound:115,load_weight:56,loadcel:[64,125],loaded_edge_snapshot_id:64,loaded_waypoint_snapshot_id:64,loadmiss:[21,64],loadsound:64,local:[2,9,11,12,13,15,16,17,31,50,51,62,63,68,75,92,98,107,108,126,151,174,176,189,191,196,197,204,206,219,221],local_grid:[75,97],local_grid_request:64,local_grid_respons:64,local_grid_servic:68,local_grid_typ:64,local_grid_type_nam:[64,97],local_ip:44,local_time_nsec:151,local_time_sec:[126,151],local_timestamp_proto:151,localauthor:51,localgrid:[37,68],localgridcli:97,localgridcommand:75,localgridresponseproto:97,localgridservic:[62,97],localhost:[21,51,56,204],localization_request:64,localization_varnam:64,locat:[2,7,10,13,14,15,16,17,18,19,20,21,24,26,28,29,34,37,39,40,43,49,50,55,56,57,58,59,61,62,64,109,134,139,172,176,189,196,201,205,206,210,213,219,221,223],lock:[23,39,62,64,71],locomot:[6,9,11,16,17,29,43,64,109],locomotion_charge_percentag:[21,64],locomotion_estimated_runtim:64,locomotion_hint:[64,109],locomotionhint:62,log:[22,24,26,43,44,50,51,55,57,60,61,63,68,70,73,75,77,79,80,82,85,94,103,108,112,113,114,129,143,159,173,177,204,209,212],log_annot:98,log_annotation_servic:68,log_client:98,log_request:143,log_respons:143,log_spot_data:199,log_token_time_remain:112,logannot:68,logannotationcli:[62,98],logannotationhandl:98,logannotationservic:[62,98],logannotationtextmessag:98,logdir:56,logger:[34,67,70,76,77,79,80,81,82,83,85,86,88,92,94,98,103,105,129,130,153],logic:[24,28,33,43,48,64,95],login:[43,50,62,64,71],logpoint:[62,119,209],logtick:64,longer:[15,19,20,26,28,29,33,39,40,41,53,57,58,59,61,62,64,80,95,109,112,126,129,158],look:[4,6,20,28,38,48,50,55,56,57,59,61,64,84,194,198,204,206,221],lookup:[39,64,92,98],lookuperror:82,loop:[13,21,45,55,57,58,59,62,64,204],loop_count:64,loos:[48,100],lose:10,loss:[10,33,56,61,62,64,95,179,204],lossi:[40,64],lost:[15,16,19,36,62,64,92],lost_detector_st:64,lot:[20,29,40,55,56,57,59,61,64,109],lover:59,low:[10,13,19,21,26,33,39,47,48,61,62,64,92,210],lower:[3,14,16,29,39,61,64,197,201,210],lower_limit:100,lower_tick_bound:153,lowercas:53,lowermost:64,lowest:[50,64],lux:4,lvi:56,mac:[26,214],machin:[16,27,35,45,51,55,57,58,60,62,64,175,181,197],maco:[54,60,61,62,189,210,212,224],macos_sdk_headers_for_macos_10:189,macroblock:64,made:[2,23,26,28,32,55,57,60,61,62,64,80,92,93,98,108,151],magic:24,magnitud:64,mai:[2,6,10,13,16,18,19,20,21,22,23,24,26,27,28,29,32,33,34,36,37,39,40,41,43,45,47,50,51,53,56,60,61,62,64,76,85,88,89,91,95,97,108,109,121,129,138,159,175,176,177,196,197,200,201,202,204,205,206,209,210,221],mail:47,main:[2,10,26,44,50,55,57,58,62,64,73,75,77,101,108,174,188,210,212,221],mainli:[26,64],maintain:[6,15,16,20,29,36,39,40,45,46,60,61,64,85,112,126,129,207,208],major:[39,61,64],major_vers:[61,64],make:[2,6,10,15,17,18,21,24,26,29,34,36,40,41,44,46,50,53,55,56,57,58,59,60,61,62,64,158,160,161,162,163,164,165,166,167,168,169,170,171,172,174,179,182,189,190,198,201,204,206,207],make_add_world_object_req:130,make_add_world_object_request:37,make_capture_paramet:94,make_change_world_object_req:130,make_data_descriptor:139,make_delete_world_object_req:130,make_edg:107,make_edge_environ:107,make_error:[41,80],make_image_sourc:94,make_recording_environ:107,make_time_query_param:78,make_time_query_params_from_group_nam:78,make_waypoint_environ:107,male:48,malform:[64,108],malfunct:64,malici:43,man:[2,26,29,57],manag:[8,23,34,36,41,42,43,45,51,64,68,70,80,95,125,126,127],maneuver:47,mani:[2,6,18,23,26,27,28,29,36,37,38,39,40,42,55,56,60,61,62,64,92,98,108,164,202,215],manip_state_attempting_raycast:64,manip_state_don:64,manip_state_grasp_fail:[58,64],manip_state_grasp_failed_to_raycast_into_map:[58,64],manip_state_grasp_planning_no_solut:[58,64],manip_state_grasp_planning_succeed:64,manip_state_grasp_planning_waiting_data_at_edg:[58,64],manip_state_grasp_succeed:[58,64],manip_state_grasping_object:64,manip_state_moving_to_grasp:64,manip_state_placing_object:64,manip_state_searching_for_grasp:64,manip_state_unknown:64,manip_state_walking_to_object:64,manipul:[5,6,39,42,55,57,62,63,64,68,171],manipulation_api_cli:[58,99],manipulation_api_command:[58,99],manipulation_api_command_async:99,manipulation_api_feedback_command:[58,99],manipulation_api_feedback_command_async:99,manipulation_api_feedback_request:[58,99],manipulation_api_pb2:[58,99],manipulation_api_request:[58,99],manipulation_api_servic:68,manipulation_camera_sourc:64,manipulation_camera_source_hand:64,manipulation_camera_source_stereo:64,manipulation_camera_source_unknown:64,manipulation_cmd_id:[58,64],manipulationapi:[64,99],manipulationapicli:[58,99,165,171],manipulationapifeedback:64,manipulationapifeedbackrequest:[58,99],manipulationapifeedbackrespons:99,manipulationapirequest:[58,99],manipulationapirespons:99,manipulationapiservic:[7,99,161],manipulationfeedbackst:58,manipulator_st:[62,64],manner:[18,29,31,60,62,221],manual:[14,17,18,26,27,43,44,56,62,64,92,161,164,196,206,207],map:[9,11,13,15,17,19,20,22,24,26,31,37,39,40,41,56,63,75,76,92,98,107,108,116,133,159,181,196,205,219,221,224],map_stat:64,map_state_ok:64,map_state_too_large_for_licens:64,map_state_unknown:64,mapnavig:11,maptoolargelicenseerror:107,mar:56,margin:[64,92,189],mark:[2,26,41,43,62,64,80,223],mark_request_cancel:80,mark_request_finish:80,marker:[2,28,189,201],mask:64,masquerad:43,mass:[2,8,29,47,62,64],mass_volume_properti:64,massless:44,master:[60,64,202],mat:[59,100],match:[15,19,26,28,31,36,37,44,53,62,64,81,85,88,90,103,107,112,127,129,136,138,139,155,202,204,206,207,219],mate:48,materi:[23,45,55,64],math:[58,59,61,62,68],math_help:[20,37,58,59,91,100],mathemat:37,mathworld:64,matric:100,matrix:[6,37,59,62,100,138],matter:[58,59,109],max:[2,4,6,8,10,28,62,64,74,89,105,107,108,112,118,125,129,210,212],max_angl:64,max_angular_veloc:64,max_attempt:64,max_chunk_s:115,max_dist:[19,64,92],max_dur:109,max_i:[58,64],max_linear_vel:59,max_linear_veloc:64,max_message_length:112,max_pos_tracking_error:64,max_receive_message_length:[74,129],max_rot_tracking_error:64,max_rotation_vel:59,max_sampl:126,max_send_message_length:[74,129],max_start:64,max_status_queue_s:[62,88],max_temp:116,max_tim:2,max_translation_met:100,max_vel:[59,64],max_vel_linear:59,max_vel_se2:59,max_work:[57,129],max_x:[58,64],max_yaw:[19,64,92],max_yaw_degre:100,max_z:64,maxima:64,maximum:[2,4,6,10,15,20,26,28,29,32,47,48,62,64,116,126,189],maximum_acceler:64,maximum_open_close_acceler:64,maximum_open_close_veloc:64,maximum_torqu:64,maximum_veloc:64,maxlen:158,mayb:[60,64],mdn:39,mean:[2,8,15,21,24,28,29,35,43,49,53,56,57,59,62,64,105,138,177,189,206,212],meant:[24,41,64,88,175],measur:[23,24,29,40,56,62,64,117],measured_pos_distance_to_go:64,measured_pos_tracking_error:64,measured_rot_distance_to_go:64,measured_rot_tracking_error:64,measurement_tim:64,mech:[64,209],mechan:[0,15,33,39,40,42,64],media:[64,114,209],media_log:[119,209],medialog:119,medialogcli:119,medialogservic:[62,119],medium:[33,42,64],meet:[4,19,58],member:[2,31,62,64,88,108],memori:[2,56,64,119,127],mention:[37,64],menu:[6,10,26,38,57,59,204,206,221],merg:[62,64,107],mesh:75,messag:[2,4,5,6,7,18,20,21,23,24,28,32,35,36,37,39,40,41,53,56,57,62,64,67,69,73,74,75,76,77,78,79,80,82,83,84,87,89,90,92,93,94,98,99,100,102,103,104,106,107,108,109,112,113,126,129,130,135,137,138,139,140,141,142,143,146,147,148,155,173,177,199,207,215,221],message_typ:[64,73,135,138,139],messagechannel:135,messageoverrideerror:155,messagetypedescriptor:[138,139],messg:64,messsagetypedescriptor:64,met:[35,53],metadata:[2,23,24,28,32,62,74,75,77,78,79,80,81,138,176],metadata_to_proto:77,metal:[62,64,107],meter:[2,6,8,10,16,19,26,37,55,59,62,64,92,107,109,151,161,164,165,171,189,190,210],method:[2,19,23,24,26,32,34,39,44,50,58,60,61,62,64,70,74,80,81,85,94,95,98,103,108,112,129,137,141,144,147,199,204],metric:[40,50,56,62,64,75,111,151,193],metricscommand:75,metronom:26,mh1:48,mh2:48,michigan:19,microphon:[62,64],microsecond:64,microservic:61,microsoft:27,mid:64,middl:[6,10,37,47,64,197,219],midwai:29,might:[2,16,19,21,24,36,38,43,44,45,47,53,55,57,58,60,61,64,189,202],millidegre:64,mimic:26,min:[2,28,57,58,62,64,107],min_angl:64,min_confid:[57,58,64],min_i:58,min_move_length_slic:2,min_temp:116,min_tim:2,min_timeout:64,min_vel:64,min_x:58,mind:[26,36],minim:[6,10,23,24,36,45,50,62,64,109],minima:64,minimum:[2,26,28,32,55,58,64,116],minor:64,minor_vers:64,mint:[39,64,71,128],minut:[2,4,23,26,28,29,56,62,64,173,176,177,212],mirror:[2,29,64],mirror_i:29,mirror_x:29,misalign:64,mismatch:[92,155],miss:[2,26,57,62,64,91,92,107,155,156,189],missing_fiduci:64,missing_input:64,missing_lease_resourc:64,missingfiducialserror:107,missinginput:156,missingleas:156,missingparametererror:155,mission:[9,12,13,16,18,19,22,31,32,33,34,43,60,63,65,153,154,155,156,157,158,159,174,175,182],mission_id:64,mission_info:64,mission_record:[62,201],mission_servic:152,missioncli:[152,153],missionresponseerror:153,missionservic:[12,62,153],mistak:59,misumi:49,misus:91,mitig:39,mitm:39,mix:[6,26,64],mjpg:221,mkdir:[55,56,57],ml_servic:[58,59],mnt:61,mobil:[5,47,59,61,63,64,109,180],mobilenet:210,mobility_command:[6,21,62],mobility_command_feedback:64,mobility_command_pb2:21,mobility_feedback:[58,64],mobility_param:[59,62,64,109],mobilitycommand:[6,21],mobilityparam:[62,109],mod:64,mode:[6,7,13,29,32,41,55,56,62,109,162,164,189,196,201,206,220,224],mode_access_point:64,mode_cli:64,mode_unknown:64,model:[3,40,46,47,50,52,55,58,59,62,64,75,101,111,159,202,210,212],model_dir:[56,57],model_main_tf2:56,model_nam:[57,58,64,202],model_path:57,modem:39,moder:28,modif:[26,56,60,62,64,221],modifi:[6,10,27,29,40,41,47,51,61,62,64,80,85,129],modul:[53,54,60,64,80,95,109,210,221],modulenotfounderror:60,moi:64,moi_tensor:64,mojav:[54,60],mold:46,moment:64,monitor:[18,34,36,40,50,56,62,64,70,78,80,105,176,201],monoton:[48,95],month:176,more:[2,5,6,9,10,12,16,18,20,24,26,28,29,31,33,34,35,37,39,40,41,43,46,47,49,50,51,55,56,57,59,60,61,62,64,71,76,92,94,95,100,103,105,107,109,159,162,174,176,181,182,183,185,190,191,201,206,207,208,212,215,219,221],mortum:64,most:[23,28,29,33,36,37,40,41,44,50,56,57,60,61,62,64,76,95,103,104,112,116,129,159,207,221],most_restrictive_travel_param:158,mostli:[57,64],motion:[2,6,28,29,47,62,64,109,164,180,190],motor:[21,26,35,40,53,60,62,64,88,105,160,161,162,163,164,165,166,167,168,169,170,171,172,188,189,198,201,211,213,220,224],motor_power_st:64,motor_power_state_error:64,motor_power_state_off:64,motor_power_state_on:64,motor_power_state_powering_off:64,motor_power_state_powering_on:64,motor_power_state_unknown:64,motorsonerror:88,mount:[18,38,42,44,46,47,48,62,64,206],mount_tform_payload:64,mous:[50,197,219],move2:29,move:[2,7,10,13,16,18,19,21,23,25,27,28,37,40,45,55,57,58,59,64,67,109,160,165,167,169,171,172,179,180,189,190,201,204,205,224],move_cmd:[58,59],move_length_slic:2,move_param_config:2,moveinfo:28,movement:[2,29,62,64,109],moveparam:28,moveparamsconfig:2,mp_safety_in:48,mp_safety_o:48,mscoco_label_map:59,msg:[82,98,158],msg_age_limit:98,msg_num_limit:98,msg_valu:64,msgtype:64,mtu:64,much:[2,7,18,26,28,29,40,55,56,61,62,64,160,190],mult:100,multi:[51,64],multipl:[2,14,16,18,19,23,27,28,29,31,32,36,37,39,40,41,44,57,61,62,64,94,95,100,109,176,189,197,207,215,221],multiplex:39,multipli:[37,59,64,115],music:[2,25,27,28],music_fil:2,music_start_slic:2,must:[2,10,13,15,16,18,19,20,21,23,26,27,28,29,33,34,35,36,39,40,41,43,45,46,47,48,50,53,56,61,62,64,74,76,78,80,85,91,95,107,109,119,130,138,153,160,161,162,163,164,166,167,168,169,170,172,174,176,177,189,191,193,194,196,201,202,203,204,206,207,208,215,219,221],mutabl:64,mutat:[37,40,41,94,95,106,113,129,130,186,223],mutate_world_object:[37,62,130,222],mutate_world_objects_async:130,mutated_object_id:64,mutateworldobject:[40,64],mutateworldobjectrequest:130,mutation_req:[37,130],mutual:[2,29,33],my_estop:61,my_graph_fold:196,my_metadata_channel:31,my_password:50,my_spot_env:[55,56,57],my_spot_v2_0_env:60,my_ssd_resnet50_v1_fpn:[56,57],my_wifi:50,myriad:34,myvari:21,name:[2,4,16,21,23,24,26,28,31,32,33,34,36,37,39,40,41,43,44,48,50,51,53,55,56,57,58,59,60,61,62,64,67,70,71,73,75,76,77,78,82,84,85,88,89,90,91,93,94,95,97,98,103,104,106,107,108,109,111,112,116,119,127,135,136,138,139,141,142,147,148,155,158,174,176,189,191,197,202,204,206,207,215,221],name_prefix:64,named_arm_position_command:64,named_arm_position_feedback:64,namedarmposit:6,namedwindow:58,namespac:[24,53,64],nano:61,nanosecond:[33,126,151,173,177],narg:57,narrow:20,nat:120,natur:[64,214],nav:[21,23,61,63,68,107,159,181],nav_pb2:92,navig:[9,10,16,18,19,20,21,22,26,40,64,92,196,200,206,214],navigate_rout:92,navigate_route_async:92,navigate_to:92,navigate_to_async:92,navigaterout:[17,62,64],navigateto:[17,62,64],navigation_feedback:92,navigation_feedback_async:92,navigationfeedback:[15,17,64],navigationfeedbackrespons:[15,92],nbsp:[43,68],nbyte:139,nearbi:[10,40,62,64,92,201],nearest:[19,40,62,64],nearli:[2,23],nearmap:64,neccessari:64,necessari:[10,34,39,41,44,46,50,51,58,62,64,74,92,95,107,137,153,176,189,207,211],necessarili:[13,29,64,221],necessesarili:64,need:[10,13,14,15,16,18,19,23,24,26,27,31,34,36,37,38,39,40,41,43,44,51,53,54,55,56,57,58,60,61,62,64,71,80,81,89,103,108,121,126,128,137,138,139,148,153,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,178,179,191,195,198,201,204,205,206,207,208,209,210,211,212,213,216,217,218,221,222,223,224],need_authent:75,neg:[2,20,28,40,48,58,64],negat:59,neighborhood:64,neither:[23,26],nest:[21,31,39],net:4,netmask:[43,64],network:[3,9,23,34,38,40,42,44,51,52,53,55,56,57,58,61,63,68,112,114,116,119,175,176,181,182,206,210,212,215],network_compute_bridg:38,network_compute_bridge_cli:[58,101],network_compute_bridge_command:[58,101],network_compute_bridge_command_async:101,network_compute_bridge_pb2:[57,58],network_compute_bridge_servic:68,network_compute_bridge_service_pb2_grpc:57,network_compute_cli:[58,59],network_compute_request:101,network_compute_serv:[57,58,59],network_compute_server_output:57,network_compute_status_external_server_error:64,network_compute_status_external_service_not_found:64,network_compute_status_rotation_error:64,network_compute_status_success:64,network_compute_status_unknown:64,network_delai:212,networkcli:120,networkcomput:[57,64],networkcomputebridg:101,networkcomputebridgecli:[58,101],networkcomputebridgeservic:101,networkcomputebridgework:57,networkcomputebridgeworkerservic:[57,101],networkcomputeinputdata:58,networkcomputerequest:[58,101],networkcomputerespons:[57,101],networkcomputerotationerror:101,networkcomputeserverconfigur:58,networkmanag:[50,51],networkservic:[62,120],neural:202,neutral:29,never:[8,23,24,26,39,48,53,64,108,190],new_endpoint:[35,64],newaxi:57,newer:[95,224],newest:64,newli:[43,64,108,203],newton:64,next:[2,14,19,20,28,50,55,56,57,58,59,62,64,94,149,206,210,212,224],nicknam:[23,60,61,62,64],nmcli:50,nnnnnnnnnn:[173,177],nnnnnnnnnnnnnnnnnnnn:[173,177],no_debug:57,nodata:31,node:[13,37,62,63,91,153,155,158,201],node_refer:64,node_spec:158,node_spec_to_short_str:158,node_st:64,nodes_pb2:[21,158],nodest:13,nodeunreferenceableerror:155,nogui:60,nois:36,nomin:[2,29,40,62,64,109],nomissionerror:153,nomissionplayingerror:153,non:[2,19,21,29,32,37,39,42,56,61,62,64,95,105,108,109,112,121,126,127,188,202],non_strict_pars:[2,28,67],none:[2,28,35,39,41,58,59,61,64,67,70,71,73,74,75,76,77,78,79,80,81,82,84,85,86,88,89,90,91,92,93,94,95,98,100,101,103,104,105,107,108,109,111,112,113,115,119,121,123,125,126,128,129,130,133,138,145,146,148,149,153,155,156,157,158,214],nonexist:89,nonexistentauthorityerror:[62,89],nonexistentserviceerror:84,nonsens:28,nonzero:129,nopatherror:92,nor:[23,26,60],norm:59,normal:[2,10,14,26,28,29,37,40,47,48,57,64,100,204],nosuchleas:[62,95],not_manag:95,not_valid_aft:64,not_valid_befor:64,notabl:[33,40],notactiveleaseerror:95,notat:37,notauthoritativeserviceerror:95,notclearederror:109,note:[0,2,10,16,19,21,23,26,27,29,31,33,34,40,41,44,47,48,50,54,55,57,58,59,60,61,64,80,94,107,108,120,158,175,176,189,190,191,196,201,202,204,206,207,208,211,214,215,221],notestablishederror:[108,126],notfounderror:[62,89],noth:[29,56,58,62,64,98,107,109,112,113,129],notic:[50,58],notimesyncerror:[92,109,130],notimplementederror:98,notincacheerror:[108,127],notlocalizedtoenderror:107,notlocalizedtoexistingmaperror:107,notpoweredonerror:109,notreadyyeterror:107,notrecordingerror:107,notset:98,now:[19,21,26,27,32,35,37,43,50,51,55,56,57,58,59,60,61,62,64,76,109,112,129,145,173,177,204,224],now_nsec:151,now_timestamp:151,nowow:64,nsec:[138,145,148],nsec_to_timestamp:151,ntp:23,num:56,num_cells_i:64,num_cells_x:64,num_class:56,num_com:64,num_data_block:[137,144],num_data_buffer_pag:64,num_detect:57,num_ev:64,num_local_grid_error:64,num_messag:[140,146],num_object:57,num_point:64,num_retri:86,num_speed_ti:[2,29],num_tick:64,num_train_step:56,number:[2,10,15,19,21,23,24,26,28,29,33,35,39,41,51,55,56,57,58,59,60,61,62,64,78,85,86,91,92,94,95,98,103,107,108,109,111,126,129,137,140,144,146,151,176,201,204,206,210,212,221],number_of_circl:[2,29],numer:[26,28,33,62,64,76,202],numpi:[55,57,58,62,65,100,197],nut:49,nvidia:[56,202,210],nx3:100,obei:[53,64],obj:[57,58,64,111],obj_label:58,obj_model:64,object:[2,6,7,20,33,34,37,41,53,55,56,57,58,59,62,63,64,67,68,69,70,73,75,76,77,78,79,80,81,82,83,85,86,87,88,92,94,95,98,99,100,103,105,106,108,109,110,112,113,126,127,128,129,133,134,136,137,138,139,140,141,142,143,144,145,146,147,148,150,151,153,157,158,159,174,180,182,189,202,206,207,210],object_detect:[56,57,59,202],object_in_imag:[57,58,64],object_rt_fram:64,object_typ:[64,67,130],objmodel:111,observ:[6,8,18,23,33,43,64,201],obsolet:62,obstacl:[6,15,16,17,40,62,64,107,189,219],obstacle_avoidance_pad:64,obstacle_param:64,obstruct:64,obtain:[61,64,82,92,93,94,104,111,126,153,202,210,212],obviou:[29,36],obvious:[59,62],occasion:[39,41,47,62,80],occassion:64,occupi:64,occur:[6,29,36,40,41,48,62,64,67,80,89,107,108,111,125,155,215],octob:33,ocu01:64,ocu:64,ocur:64,odd:64,odom:[6,18,37,62,64,91,100,109,190],odom_frame_nam:62,odometri:[20,37,62,64,109],off:[10,11,14,21,26,29,33,34,35,36,38,40,43,55,58,59,60,62,64,80,85,95,103,105,108,121,176,179,188,189,196,202,204,206,211,213,221,224],offer:[3,8,26,33,39,40,43,44,57,64,120],offici:[56,224],offlin:89,offload:202,offset:[2,21,23,24,29,40,59,62,64,92,139,189],offset_dist:64,often:[2,6,35,37,40,55,56,57,59,61,64,80,82,89,125,206,223],oil:18,okai:56,old:[18,24,33,39,62,64,71,74,85,206],old_offset:64,old_zero:64,older:[62,64,80,95,209],older_than_tim:80,oldest:[33,64],omit:[10,64],on_lease_use_result:95,onboard:[6,18,34,207],onc:[6,13,14,15,17,18,20,21,23,26,27,28,29,33,35,36,40,41,43,44,55,56,57,58,61,62,64,103,125,179,196,207,213,218],one:[4,6,13,15,16,18,19,21,23,24,26,28,29,31,33,35,36,37,39,40,43,53,55,58,59,60,61,62,64,71,82,84,85,88,94,98,103,107,108,109,151,155,173,177,189,190,191,193,197,201,202,207,210,215,219],one_line_str:158,oneof:[28,64,109,158],ones:[18,34,62,64,159],onli:[2,6,7,10,16,19,21,23,24,26,28,29,31,32,33,36,37,39,40,41,43,50,51,53,55,56,57,58,60,61,62,64,71,74,78,81,86,88,93,100,103,104,109,112,113,129,158,176,177,189,197,204,206,207,215,221,224],onlin:[26,38,55,56,57,206],onset:64,onset_timestamp:64,onto:[29,40,44,45,57,60,62,64,103,109,153,179,207,210],opaqu:33,open:[2,5,6,7,8,11,19,26,27,33,48,55,56,57,59,60,61,62,64,87,161,176,180,189,197,204,206,207,214,221],open_door:87,open_door_async:87,open_door_feedback:87,open_door_feedback_async:87,open_fract:109,open_port:204,opencv:[41,55,56,58,62,221],opendoor:64,opendoorcommandrequest:87,opendoorcommandrespons:87,opendoorfeedback:64,opendoorfeedbackrequest:87,opendoorfeedbackrespons:87,opendoorrespons:64,opengl:[62,214],oper:[4,5,6,8,9,10,12,13,17,18,20,21,22,36,39,40,43,45,48,53,55,60,61,62,64,75,80,82,83,88,92,98,108,177,188,189,199,201,206,208,210,212,214,220,221],operator_com:[64,108],operator_messag:64,operatorcom:[24,173],operatorcommentcommand:75,oppos:59,opposit:29,ops:95,opt:[36,176,206],optic:64,optim:[62,64],option:[6,17,21,23,27,28,29,31,32,38,39,40,41,50,53,55,56,57,58,59,60,61,74,75,78,80,85,86,92,95,108,109,112,113,115,116,119,129,138,139,177,196,201,202,204,206,210,212,215,217,219,221],orang:202,order:[6,16,18,21,26,28,33,34,39,43,45,50,51,62,64,80,92,108,129,150,159,190,196,204,217,218],ordinarili:28,org:[50,51,60,64],organ:[32,56,64,210,212],orient:[2,6,7,20,21,29,37,40,48,61,62,64,107,109,150,191,219],origin:[2,6,26,29,33,37,38,43,61,62,64,78,212],original_error:89,original_valu:158,orthogon:59,osc:206,oscil:29,other:[0,2,6,7,9,10,16,17,18,19,21,22,23,24,26,27,28,29,32,33,36,37,39,40,42,43,44,47,53,56,57,60,61,64,67,69,76,77,79,81,82,83,84,85,86,87,91,92,93,94,95,98,99,100,104,105,108,109,129,130,138,153,176,177,183,185,191,197,202,204,206,210,211,215,219,221],other_data:64,other_leas:95,other_own:95,other_quat:100,otherwis:[2,13,20,21,36,57,58,64,76,77,88,89,91,95,105,108,111,112,126,176,196,204,206,221],our:[0,3,6,21,26,42,50,52,53,54,55,56,57,58,59,60,61,62,64],ourselv:57,out:[6,12,16,21,22,23,26,29,32,35,36,37,39,41,53,55,56,57,58,59,60,61,62,64,71,75,85,88,92,93,104,105,109,111,117,119,125,126,130,151,159,191,197,201,204,207,215,219],out_obj:57,out_proto:57,outag:62,outbound:50,outcom:32,outdoor:45,outer:[45,53],outermost:64,outfil:[133,134,137,138,149],outlier:62,outlin:[34,36,51,64],output:[2,30,38,39,41,48,56,57,58,59,60,61,62,64,73,75,80,93,94,98,100,164,175,202,204,210,212,221],output_directori:57,output_filenam:73,output_path:56,outsid:[10,20,26,28,34,36,39,40,53,64,89,202],outstand:64,outstretch:6,outward:29,oval:64,over:[2,3,6,15,19,23,26,31,35,39,40,46,47,50,52,53,55,56,57,58,61,64,86,105,107,109,116,160,162,164,179,204,210,212],overal:[18,29,33,47,62,64,80],overcom:64,overcurr:48,overh:[36,40],overhang:47,overhead:62,overlai:[62,64,205],overlap:[26,56],overmold:46,overrid:[2,41,62,94,109,120,133,153,155],overridden:[15,64,98,125],override_external_force_vec:109,override_hold:64,override_mobility_param:64,override_not_hold:64,override_request:64,override_unknown:64,overridegrasp:64,overriding_messag:155,overview:[3,25,30,41,50,55,57,181,183,186,190,221],overwhelm:[64,206],overwrit:[55,64,107],overwritten:[6,64,105,207],own:[6,18,23,26,31,33,36,37,41,43,54,55,57,62,64,70,76,95,139,176,206],owner:64,ownership:[2,64,86,92,105,107],p1_x:64,p1_y:64,p1_z:64,p_gnd:48,pace:[2,62],pace_2step_param:2,pack:[6,21,37,38,48,57,58,64,80],packag:[41,53,56,57,61,62,112,189,218,224],packet:[39,43],pad:[64,224],page:[23,27,33,37,43,44,50,55,56,57,58,59,62,64,103,108,177,207,221],page_id:[64,83],pai:45,paid:39,painstakingli:56,pair:[2,4,24,29,35,48,64,76,80,138,139,143,196],palm:[6,7,58,64],pan:[21,26,64,122,197,219],pan_limit:64,pane:26,panel:[160,161,162,163,164,165,166,167,168,169,170,171,172,198],pano:[31,62,64,209],panoram:[31,62],panorama:64,paradigm:39,parallel:[189,210,211],param:[1,2,28,58,59,62,64,77,78,79,91,109,129,153],paramet:[2,7,10,18,19,21,25,27,28,29,31,33,40,41,55,56,58,59,61,62,63,67,69,70,71,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,90,91,92,93,94,95,97,98,99,101,102,103,104,105,107,108,109,111,112,113,116,119,125,126,129,130,137,138,139,141,145,146,147,151,153,155,156,158,164,212],parameter:[40,64],parameter_valu:64,parent:[6,37,41,61,62,64,75,91,133,158],parent_frame_nam:[64,91],parent_tform_child:[37,64,91],parentedg:37,park:20,parlanc:35,pars:[11,21,24,28,33,53,62,64,75,93,136,149,151,197],parse_arg:[55,57,58],parse_datetim:151,parse_timespan:151,parseerror:[136,137,141,144,147,149],parser:[28,55,57,58,75,129],part:[2,11,19,26,29,31,32,40,41,43,53,55,62,64,81,109,138,159,221],parti:[34,36,60,106,204],partial:[6,64],particualar:64,particular:[2,13,17,20,23,28,31,32,45,53,62,64,81,84,92,108,140],particularli:[26,38,62],partit:56,pass:[26,34,39,41,43,51,59,60,61,64,71,88,92,93,95,102,103,104,109,112,113,126,176,191,200,205,206,210,212,213,217,221,224],passphras:[64,119],password:[23,26,27,39,41,43,44,50,55,57,58,59,60,61,64,71,108,119,129,160,161,162,163,164,165,166,167,168,169,170,171,172,173,175,176,177,178,179,188,189,190,191,192,193,194,195,196,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,215,216,217,218,219,220,221,222,223,224],past:[58,62,64,80],past_tick:153,patch_level:64,path:[2,6,10,15,16,18,20,31,53,55,57,58,60,62,64,92,112,174,179,196,201,202,210,212,221],path_on_your_comput:60,path_to_downloaded_map:196,path_to_miss:205,path_to_mission_fil:200,path_to_pb:[210,212],path_to_python3_execut:210,path_to_your_map_directori:197,pathnam:60,patrol:64,pattern:[2,29,39,53,64,204,210,212,224],paus:[17,21,40,64,153,164],pause_miss:153,pause_mission_async:153,pause_tim:64,pause_time_sec:153,pausemiss:[21,64],pay_reg_cli:103,payload:[0,3,6,19,20,21,23,28,30,32,36,39,40,46,49,50,52,54,63,68,75,85,90,94,105,108,129,159,176,206,208,221],payload_cli:43,payload_credenti:[176,206],payload_descript:207,payload_estimation_feedback:64,payload_estimation_request:64,payload_guid:[44,64,207,221],payload_guid_and_secret:[176,206],payload_ip:221,payload_nam:207,payload_ports_power_st:64,payload_ports_power_state_off:64,payload_ports_power_state_on:64,payload_ports_power_state_unknown:64,payload_proto:43,payload_registr:103,payload_registration_cli:[43,108],payload_registration_servic:68,payload_result:64,payload_secret:[44,64,207],payload_servic:68,payloadalreadyexistserror:103,payloadcli:[43,102],payloadcommand:75,payloaddoesnotexisterror:103,payloadestim:64,payloadestimationcommand:62,payloadlistcommand:75,payloadnotauthorizederror:103,payloadregistercommand:75,payloadregistr:[68,103],payloadregistrationcli:[43,103],payloadregistrationkeepal:103,payloadregistrationresponseerror:103,payloadregistrationservic:[62,103],payloadservic:[62,102],paylod:64,pazwierd:60,pb_enum_obj:158,pb_type:158,pbtxt:[56,57,58,59],pcba:45,pdb_root:[35,61,88],peak:8,peer:[39,61],peopl:[6,10,55,58,64,189,202,210,212],per:[2,4,21,26,28,29,34,36,37,56,62,64],perceiv:[62,64,189],percent:[64,93,94],percentag:[21,57,62,64,115],percept:[36,40,62,64,92,130,159,189,219],perfect:64,perform:[2,16,18,19,21,29,33,38,39,40,41,50,51,56,57,59,61,64,67,77,79,80,85,92,126,164,175,204,206,207,210,212,214,221],period:[15,20,23,29,39,40,41,61,62,64,70,85,86,88,103,105,119,125,200],period_sec:70,periodic_sec:70,perman:[19,174],permiss:[23,25,43,44,51,60,89,127,207],permission_requir:64,permissiondeniederror:[62,89],permit:64,persist:[16,17,18,19,33,39,44,64,103,107,108,206],person:[8,55,58,59,186,206,210],person_model:59,perspect:[44,61,62],pertain:[29,36,62,64],pertin:36,pgm:[62,93],phase:[17,29,64],phone:206,php:[2,64],physic:[39,40,43,53,62,64,215],pick:[2,7,8,29,38,55,57,59,62,64,165,180],pick_auto_gaz:64,pick_auto_walk_and_gaz:64,pick_no_auto_walk_or_gaz:64,pick_object:64,pick_object_execute_plan:64,pick_object_in_imag:[58,64],pick_object_ray_in_world:64,pick_plan_onli:64,pick_vec:58,pick_walk_gaze_unknown:64,pickobjectinimag:58,pickup:[59,64],pictur:[14,26,37,48,50,55,58,60,62,64,165,171,206],pid:[51,57,64,88,207],pidfil:51,piec:[31,41,59,61,64,80,81,164,207],piecewis:64,piksi:[41,176],pil:[57,61,198],pil_imag:57,pillow:198,pilot:10,pin:49,pinch:45,ping:[32,50],pinhol:[58,64],pink:47,pinmax:48,pip3:210,pip:[55,56,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,188,189,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224],pipelin:[56,57,183,210],pipeline_config_path:[56,57],pitch:[2,37,43,61,62,64,100,150,224],pivot:29,pivot_cent:2,pivot_front:2,pivot_hind:2,pivot_unknown:2,pixel:[7,40,41,57,58,62,64,94,189,223],pixel_format:[57,64],pixel_format_depth_u16:64,pixel_format_greyscale_u16:[62,64],pixel_format_greyscale_u8:[57,64],pixel_format_rgb_u8:[57,64],pixel_format_rgba_u8:64,pixel_format_unknown:64,pixel_xi:[58,64],pixelformat:41,pkg:189,pkla:51,pl_safety_in:48,pl_safety_o:48,place:[2,6,11,13,17,18,20,21,26,29,39,55,56,58,59,62,64,129,151,201],placehold:189,placement:[62,64],plai:[2,9,11,21,26,58,62,64,115,153,200,205,209],plain:[24,33,64],plan:[19,47,62,64],planar:[64,189],planar_ground:64,plane:[4,29,37,47,62,109],planner:64,plastic:45,plate:[6,62,64],platform:[0,19,23,40,42,43,44,53,62,64,174],play_miss:153,play_mission_async:153,play_sound:115,play_sound_async:115,playabl:64,playback:[17,21,62,64,204],player:[21,26],playmiss:[21,64],playsound:64,pleas:[0,20,26,32,41,50,51,60,64,71,98,160,161,162,163,164,165,166,167,168,169,170,171,172,176,188,189,194,198,206,210,211,212,221,224],plenti:[26,28],plot:212,plu:[21,32,62,64],plug:[50,64,206,221],plugin:[32,34,44,63,64,68,74,164,183,218],plugin_file_nam:176,plugin_test:[41,215],pluginerror:41,pod:[24,33,64,132,138],pod_series_read:144,pod_series_writ:145,pod_typ:[64,138,139,144,145],podseriesread:144,podserieswrit:145,podtypedescriptor:[138,139,144],podtypeenum:138,point1:57,point2:57,point3:57,point4:57,point:[2,6,10,16,20,21,23,24,26,37,39,40,42,45,50,51,54,55,56,57,58,59,60,63,64,68,100,107,108,109,112,151,174,176,189,190,197,201,206,210,212,223],point_cloud:104,point_cloud_request:[64,104],point_cloud_respons:64,point_cloud_sourc:[64,104],point_cloud_source_nam:[64,104],pointcloud:[41,104,176],pointcloudcli:[104,218],pointclouddataerror:104,pointcloudrequest:104,pointcloudresponseerror:104,pointcloudservic:104,pointcloudsourc:104,pointer:13,polar:64,polici:[44,64,128,204],polkit:51,poll:[14,23,40,53,58,59,64,218],polycarbon:45,polyethylen:45,polygon:[57,58],polylin:[57,58],polynomi:64,pool:[39,80],poor:[10,39,57,64],poorli:[19,91],pop:[55,57,62],popul:[2,20,36,39,41,62,64,90,94,129,207],populate_response_head:129,popup:62,port:[4,8,26,34,36,39,42,44,46,50,51,57,59,61,62,64,74,85,105,129,175,176,202,204,206,207,221],port_numb:[176,206,221],port_the_plugin_will_monitor:176,portain:[50,176,221],portbulk:48,portion:[10,29,56,57],pos:64,pos_fl_rt_fram:109,pos_fr_rt_fram:109,pos_hl_rt_fram:109,pos_hr_rt_fram:109,pos_interp_cub:64,pos_interp_linear:64,pos_interp_unknown:64,pos_interpol:64,pose1:59,pose2:59,pose:[2,17,18,19,20,28,29,37,55,60,62,64,86,91,100,107,109,167,198,220,224],pose_3d:100,pose_dist:[58,59],pose_to_xyz_yaw:100,pose_trajectory_in_task:64,posit:[2,4,6,7,10,15,18,19,20,21,26,28,29,31,37,40,48,49,55,58,59,61,62,100,107,109,116,122,161,162,164,168,169,172,189,190,201,206,210,219],position_constraint:64,positions_carri:64,positions_readi:64,positions_stow:64,positions_unknown:64,possess:64,possibl:[2,6,8,10,14,17,19,23,26,28,34,36,39,40,45,50,53,59,61,62,64,77,79,89,92,164,176,177,179,202,204,206,221],post:[33,56,64,201,202],postiv:64,postprocess:[64,123],postur:29,potenti:[6,10,26,40,45,60,64,94],power:[8,10,14,21,23,24,26,35,42,43,44,47,50,53,57,60,63,68,75,88,95,103,108,109,111,114,125,179,188,189,196,201,204,206,207,211,213,215,220,221,224],power_cli:[105,108],power_command:105,power_command_async:105,power_command_feedback:105,power_command_feedback_async:105,power_command_id:[64,105],power_cycle_robot:105,power_off:[61,105,108],power_off_mission_servic:204,power_off_motor:105,power_off_payload_port:105,power_off_robot:105,power_off_wifi_radio:105,power_on:[21,61,105,108],power_on_miss:21,power_on_motor:105,power_on_payload_port:105,power_on_wifi_radio:105,power_pb2:21,power_servic:68,power_st:[21,61,64],power_statu:64,powercli:[105,121],powercommand:[53,64,75],powercommandfeedback:[53,64],powercommandrequest:[21,40],powererror:105,powerpayloadscommand:75,powerresponseerror:[105,108],powerrobotcommand:75,powerservic:[53,62,105,121],powerst:[40,61,62],powerwifiradiocommand:75,practic:[17,36,64],pre:[35,36,50,55,56,59,60,61,64,86,210,212],pre_move_cycl:[2,29],preced:[2,27,29,64],precis:[15,23,28,36,49,56,62,64,151],precompil:56,precondit:53,preconfigur:51,predefin:[26,64,210,212],predetermin:26,predict:[24,56,57],prefer:[34,41,53,61,64,177,221],preferred_joint_configur:64,prefix:[37,53,64,82,93,107,112,207],preinstal:[60,210],preload:44,premad:26,prep:[29,62,64,86],prep_pose_behavior:[14,64,86],prep_pose_only_pos:64,prep_pose_skip_pos:64,prep_pose_undock:[14,62,64],prep_pose_unknown:64,prep_pose_use_pos:64,prepar:[7,56,64,171],prepend:64,present:[31,39,48,61,62,64,84,165,171],preserv:[16,33,43,64],preset:[57,64,207],preset_configur:64,preset_nam:64,press:[7,26,46,50,55,57,59,64,188,201,206,224],press_force_percentag:64,pretti:56,pretty_print:75,prevent:[6,14,19,23,28,36,39,40,41,45,61,62,64,88,91,92,95,129,189,206,211],preview:25,previou:[2,10,21,28,29,35,36,55,56,57,58,59,61,62,64,82,85,86,126],previous:[51,62,64,71,74,82,86,88,95,105,109,138],previous_estim:64,previous_leas:64,previous_round_trip:[64,126],primari:[10,51,53,61,64],primarili:[29,33],primit:[19,39,64],princip:64,principal_point:64,principl:[36,61],print:[19,41,43,55,56,57,58,59,62,75,76,125,173,191,201,204,215,224],print_respons:76,prior:[15,18,20,28,29,40,62,64,204,210],prioriti:[39,60],prism:219,privat:[43,64,88,176,206,207,221],privileg:50,probabl:[55,56,64],problem:[15,23,36,39,50,53,56,57,62,64,67,76,77,79,80,82,83,84,85,87,88,90,92,93,97,98,101,102,103,104,105,108,109,111,126,130,137,141,144,147,149,153,209,214],problemat:39,proce:[13,33,50,80,95],procedur:[26,39,50,61,64,89,221],process:[9,13,16,17,18,21,22,24,26,32,33,34,36,40,41,43,50,53,57,61,62,64,76,77,79,80,85,101,107,123,126,202,206,207,210,212],process_img_req:58,process_kwarg:76,process_thread:57,processed_images_queu:212,processing_delai:212,processing_skip:212,processor:[67,68,76,77,79,81,82,83,86,92,95,98,105,108,130,153,206,212],produc:[7,18,19,28,45,56,64,164,176],product:[33,59,64,206],profil:29,program:[0,11,19,26,36,38,39,41,53,54,55,60,62,64,85,108,159,176,177,183,189,191,192,193,194,196,197,199,200,203,206,208,210,212,214,217,219,221,224],programm:[60,62,89],programmat:[32,36,62,64,207],progress:[40,53,62,64,80,159],project:[4,24,37,40,49,50,64],promis:58,prompt:[20,21,23,56,60,62],pronounc:[2,29],propag:62,proper:[26,34,48,60],properli:[6,33,35,36,43,50,60,62,160,161,162,163,164,165,166,167,168,169,170,171,172,174,179,198,206,207,212,213,224],properti:[2,21,40,59,62,64,67,70,76,88,95,100,108,109,126,130,133,134,138,139,140,141,142,144,145,146,147,148,149,153,223],proprietari:19,protect:[4,6,8,46,64,80],proto3:53,proto:[6,13,19,27,28,29,34,37,38,39,40,41,43,52,53,56,57,58,59,60,61,62,68,70,77,80,82,84,85,88,91,93,94,98,100,102,106,108,109,110,113,124,126,132,133,138,139,151,152,207,215],proto_enum_to_result_const:158,proto_from_result:158,proto_from_tupl:158,proto_messag:129,proto_msg:158,proto_nam:141,proto_typ:[140,142],protobuf:[2,4,24,26,28,34,35,39,41,56,57,58,60,64,67,68,76,77,80,82,92,93,94,95,98,100,103,104,107,109,113,119,126,132,135,141,142,143,151,152,156,158,173,196,199,207,217,221],protobuf_channel_read:146,protobuf_class:141,protobuf_read:[146,147],protobuf_series_writ:148,protobuf_typ:[146,147,148],protobufchannelread:146,protobufread:147,protobufserieswrit:148,protoc:[56,207],protocol:[0,21,25,43,53,57,60,61,62,120,175,210],prototyp:64,provid:[0,3,4,7,9,11,13,14,15,17,18,19,20,21,23,24,25,26,27,28,29,32,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,56,59,60,61,62,64,71,74,77,79,80,86,88,89,92,93,94,95,100,103,104,108,109,112,113,155,156,158,188,189,191,196,202,204,206,207,210,215,217,221],provis:39,proxi:[62,89],proxim:[6,39],proxyconnectionerror:89,pseudo:[6,41,64],ptz:[21,31,62,63,114,121,209],ptz_desc:122,ptz_posit:64,ptzclient:[121,122],ptzservic:[62,122],publish:[44,62,64,98],pull:[6,48,59,64,212],pure:[23,24,43,64],purpl:205,purpos:[40,44,55,64,74,221],push:[7,60,62,64,210,212],put:[2,6,10,41,45,53,55,56,57,59,62,64,108,109,196],puttext:[57,58],pwd:[175,206,221],pwr:48,pycocotool:56,pyjwt:65,pyopengl:218,pypi:60,pyplot:218,pyqt5:[55,218],pyqt:218,python2:60,python3:[41,55,60,61,62,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224],python:[0,2,11,12,21,23,25,26,27,28,34,35,36,39,41,43,44,53,55,56,57,58,59,62,64,76,98,151,160,161,162,163,164,165,166,167,168,169,170,171,172,174,176,188,189,192,196,197,198,199,200,204,206,207,209,210,211,212,214,218,219,224],python_out:[56,207],python_type_to_pb_typ:158,python_var_to_valu:158,pythonpath:210,qt5:218,qtopengl:218,quadrant:64,qualiti:[3,20,40,61,62,64,92,93,94,114,123,210],quality_perc:[41,64,93,94],quantiti:[53,64],quarter:26,quasi:[33,64],quat:[59,62,100],quat_to_eulerzyx:100,quaternion:[6,37,59,61,100],queri:[15,21,24,31,33,38,40,43,62,64,70,75,78,80,81,82,83,93,94,104,108,111,125,176,177,190,191,193,202,206,218],query_nam:70,query_param:78,queryabl:[108,119,221],question:[21,26,60,62,153,181],question_id:[64,153],questionalreadyansw:153,queu:[62,64],queue:[57,62,64,210,212],queue_statu:[62,64],queued_disk:64,queued_rend:64,queued_unknown:64,quic:39,quick:[23,50,58,60,62,64],quickli:[15,18,29,40,41,50,53,55,62,64,206,212],quickstart:[0,54,55,56,61,62,159,160,161,162,163,164,165,166,167,168,169,170,171,172,198,212,224],quiet:57,quit:[61,196],race:64,rad:[2,29,43,64],radial:[10,64],radian:[2,6,53,58,64,92,100,109,150],radio:[39,50,62,64,75,105],radiometr:[62,64],radiu:[2,29,46,64],rai:64,rail:42,rais:[2,29,36,39,41,43,61,62,64,71,76,77,79,80,82,83,84,85,86,87,88,90,91,92,93,95,97,98,101,102,103,104,105,108,109,111,112,125,126,130,133,137,138,139,141,144,145,147,148,149,151,153],ram:[64,92],ramp:162,ran:[59,64,175],random:[2,29,33,44,61,64,176,204,206,221],random_rotate_param:2,randomli:[2,56,64],randomrot:2,rang:[2,4,10,26,28,33,39,40,42,47,48,57,62,63,64,78,122,126,189],rapid:53,rapidli:[94,212,215],rare:85,rate:[2,23,39,62,64,206,210],rather:[2,6,29,31,39,40,50,53,58,62,64,109],ratio:[29,56],rational:53,raw:[24,37,40,41,57,61,62,64,93,94,197,210,212],raw_images_queu:212,ray_end_rt_fram:64,ray_start_rt_fram:64,raycast:64,rcnn:210,reach:[2,6,8,15,21,26,29,39,43,50,53,58,62,64,89,92,98,109,149,182,190,204,207],reachabl:64,react:36,reaction:64,read:[0,3,6,21,24,26,28,33,39,43,52,53,54,60,62,64,67,75,88,95,102,104,112,127,133,137,139,140,141,144,146,147,149,155,206,210,212],read_anywai:64,read_checksum:[133,149],read_data_block:149,read_next_block:149,read_sampl:144,readabl:[32,40,41,64,107,207],reader:132,readi:[6,26,55,56,57,60,64,109,168,224],readili:[26,50],readm:[159,189],real:[13,17,26,34,39,55,57,62,64,210,212],realist:62,realli:[59,61],realsens:64,rear:[14,39,43,44,47,64,109,160,161,162,163,164,165,166,167,168,169,170,171,172,198],rearrang:55,rearward:64,reason:[2,14,19,23,24,39,55,57,61,64,206],reboot:[16,18,44,62,64,206,207],recal:59,recalcul:64,recalibr:64,receiev:64,receiv:[6,7,10,23,34,35,38,39,41,43,60,61,62,64,71,74,82,85,88,92,98,108,109,112,113,129,161,189,204,210,212],recenc:95,recent:[10,40,41,60,61,62,64,95,104,196],recenter_angl:100,recharg:14,reciev:64,reckon:64,recogn:[19,41,55,62,64,89,151],recommend:[8,14,16,28,41,44,45,47,50,54,60,61,62,64,80,164,196,207,218],reconfigur:[103,202],record:[9,11,13,16,17,18,19,20,21,22,23,31,32,56,63,68,80,92,98,119,129,159,181,197,207,209],record_level:98,record_level_to_proto_level:98,record_to_msg:98,record_typ:119,recorddatablob:[33,64],recorddatablobsrequest:62,recordev:[33,64],recording_command_lin:[11,16,17,62,196],recording_environ:[64,107],recording_servic:68,recordingenviron:107,recordingserviceresponseerror:107,recordoperatorcom:[33,64],recordsignaltick:[33,64],recordtextmessag:[33,64],recordtyp:119,recov:[18,62,64,121,179],recover:[24,64],recoveri:[18,64],recruit:6,rectangl:[29,55],rectangular:[64,219],rectilinear:64,red:[10,16,28,32,60,64,88,188,197,219],reduc:[6,19,20,26,40,41,44,47,62,64,210,212,221],reenter:57,refer:[0,4,9,11,12,13,16,19,20,25,26,31,32,37,39,40,43,44,48,52,54,60,62,64,92,100,176,188,197,206,210,221],referenc:[2,31,41,48,61,64,155,189],reference_id:[41,64],reference_tim:64,refine_fiducial_result_with_icp:[64,92],reflect:[62,64],refresh:[23,39,64,74,128],refresh_interv:123,refreshingaccesstokenauthmetadataplugin:74,refreshinterv:64,refus:[6,20,55,62,64],regain:62,regard:[21,62,64],regardless:[29,37,64,160],regen:48,region:[15,20,40,46,64,107],regist:[23,32,33,35,36,38,40,41,42,44,50,54,57,62,64,75,82,84,85,88,94,102,103,107,108,112,136,138,139,143,145,148,176,178,185,188,191,202,203,204,206,208,215,221],register_async:88,register_payload:[43,103],register_payload_and_authent:108,register_payload_async:103,register_service_cli:[58,112],register_signal_schema:82,register_signal_schema_async:82,register_with_robot:57,registerestopendpoint:64,registerestopendpointrequest:35,registerpayload:[43,64],registerservic:[43,64],registersignalschema:[33,64],registr:[23,34,36,40,44,50,57,61,62,63,64,68,75,82,90,159,176,185,204,206,215,221],registraion:62,registration_interval_sec:103,registri:[102,103],regrasp:64,regular:[2,36,40,61,64,70,95],regularli:[18,61,64,71,95,103],reiniti:64,reinstal:60,reject:[2,23,28,62,64,89,95,108,109],rel:[2,6,13,16,18,19,20,28,29,31,36,37,40,43,53,55,59,62,64,92,109,126,164,190,197,217],relat:[12,20,28,36,37,40,61,62,64,75,91,92,95,129,136,137,176,185,214],relationship:[16,37,61,64,197],releas:[0,2,5,20,24,27,32,33,39,41,44,50,64,98,188,189,201,206],relev:[20,35,36,37,64,154],reli:[15,17,18,20,40,80],reliabl:[26,29,36,39,55,62],reload:51,reloc:62,remain:[2,14,15,28,29,40,62,64,112,189],remainafterexit:51,remaining_rout:64,remap:64,remapping_const:64,remedi:64,rememb:[20,36,60,160,161,162,163,164,165,166,167,168,169,170,171,172,198],remind:[26,206],remot:[21,34,39,50,51,61,62,63,89,98,107,152,174,181],remote_cli:156,remote_cloud_statu:64,remote_mission_cli:204,remote_mission_servic:[11,62],remote_servic:152,remotecli:[62,152,156,204],remotecloudfailurenodataerror:107,remotecloudfailurenotindirectoryerror:107,remotegrpc:21,remotemiss:[64,156],remotemissionservic:[11,12,62,156],remoteservic:43,remount:64,remov:[18,19,40,48,50,57,64,78,80,85,95,98,119,178,189,206,211],renam:[58,60],rench:64,render:[64,219],rendit:37,renew:62,repeat:[19,21,23,28,29,34,41,49,62,91,129,189,191,219],repeatedli:36,repetit:64,replac:[50,55,56,59,62,64,88,105,189,206],replai:[9,10,15,17,18,19,32,62,181],replay_miss:[62,200,205],repli:[57,64],repo:[0,189],report:[14,18,20,32,36,37,40,53,57,58,59,62,64,80,125,212],reposit:[62,64,109,213],repositori:[0,56,112,189],repres:[2,16,18,19,24,28,36,37,39,40,41,44,53,61,62,64,70,73,82,88,91,92,94,100,107,109,112,126,150,151,189,196,197,207,221,223],represent:[21,26,31,39,40,61,62,64,91,158],reproject:[40,64],req:[39,76],reqeuest:64,request:[2,5,7,11,16,21,23,24,28,33,34,35,37,39,40,41,43,46,47,50,53,57,58,59,61,62,69,75,76,77,78,79,80,84,85,87,88,89,92,93,94,95,97,99,101,103,104,105,106,107,108,109,111,113,125,126,129,130,135,140,143,157,161,163,165,166,168,169,170,172,176,193,196,202,206,207,215,221,223],request_cycle_robot:64,request_head:[39,61,64],request_id:[64,77,78,79,80],request_iter:76,request_live_imag:[64,92],request_live_point_cloud:[64,92],request_live_robot_st:[64,92],request_live_terrain_map:[64,92],request_live_world_object:[64,92],request_manag:80,request_off:[40,64],request_off_motor:64,request_off_payload_port:64,request_off_robot:64,request_off_wifi_radio:64,request_on:[21,40,64],request_on_motor:64,request_on_payload_port:64,request_on_wifi_radio:64,request_preserv:64,request_queu:57,request_received_timestamp:[39,61,64],request_timestamp:[61,64],request_trim_for_log:76,request_unknown:64,requestabortederror:92,requestcancellederror:80,requested_slic:2,requestfailederror:92,requesthead:[2,39,53,113],requestiddoesnotexisterror:[77,79],requestmanag:80,requestor:64,requestst:80,requir:[2,7,13,14,15,17,19,21,23,24,25,26,28,29,33,34,35,36,39,40,41,42,43,44,45,49,50,51,55,56,57,58,59,61,62,64,77,86,89,103,105,106,107,109,129,156,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,188,189,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,207,208,209,210,211,212,213,215,216,217,218,219,220,221,222,223,224],require_align:[64,107],require_fiduci:[64,107],required_rol:88,requirements_client_onli:202,requirements_tensorflow_server_cpu:202,requirements_tensorflow_server_gpu:202,rerout:34,rerun:206,research:[56,57,59,202],resend:[15,40,92],reserv:[2,24,33,53,64],reset:[35,48,62,64,85,121,122,197,207,219],reset_servic:85,reset_service_registr:85,resett:48,reshap:[57,58],resili:62,resist:[8,48,64],resistor:48,resiz:26,resnet50:56,resolut:[43,62,64,206,210],resolv:[36,60,62,64,89,112],resons:64,resourc:[57,61,64,92,95,98,107,112,156,221],resource_already_claim:64,resource_list:95,resource_path_glob:112,resourcealreadyclaimederror:95,resp:[58,76],respect:[2,6,13,20,29,33,37,40,61,62,64,100,109,180,187,202],respond:[21,34,35,36,40,41,43,53,61,62,64,80,94,101,215],respons:[2,6,14,20,33,35,36,39,41,53,57,58,60,61,62,64,67,69,70,71,76,77,79,80,81,84,85,88,89,90,92,93,95,101,103,104,105,106,107,108,109,111,112,113,125,126,127,129,130,135,140,143,153,156,157,174,176,191,206,207,215],response_from_challeng:88,response_iter:76,response_queu:57,response_timestamp:[39,61,64],response_trim_for_log:76,responsecontext:[62,113,157],responseerror:[62,67,71,76,77,84,85,86,88,89,90,92,93,95,101,103,104,105,107,108,109,125,153,156],responsehead:[2,39,53,113,129,157],responsetoolargeerror:[62,89],rest:[23,24,31,32,33,39,53,55,62,64,78,80,176,215],restart:[21,44,50,59,62,64,85,98,103,153,158,176,204,206,221],restart_after_stop:64,restart_miss:153,restart_mission_async:153,restartmiss:[21,64,153],restor:[10,56],restrict:[13,64],restrict_fiducial_detections_to_target_waypoint:[62,64],result:[13,15,18,20,23,26,28,31,36,38,47,55,56,57,58,61,62,67,70,76,80,92,95,110,125,154,158,175,176,191,202,204,206,221],result_constant_to_proto_enum:158,result_error:64,result_failur:64,result_run:64,result_success:64,result_unknown:[64,158],resultact:51,resultani:51,resultfromproto:158,resultinact:51,results_from_proto:158,resum:39,retain:[64,95],retain_leas:95,retain_lease_async:95,retainleas:64,retarget:64,reticl:116,retri:[21,62,86,89,179],retriev:[30,32,33,36,41,62,64,78,84,94,95,112,115,117,118,119,121,124,137,176,191,209],retrieve_raw_data:119,retrieverawdata:64,retryableunavailableerror:[62,89],return_leas:[58,59,95],return_lease_async:95,return_to_start_pos:[2,29],returned_leas:95,returnleas:64,reus:[16,50],revamp:62,reveal:14,revers:[2,29],revert:64,review:[0,12,33,50,64,206,214],revis:[56,60,61],revok:[64,95,125],revokedleaseerror:95,revolut:64,rfc:176,rgb24:64,rgb:[2,8,57],rgba:64,rhs:[21,64],rich:19,ricoh:[34,41,44,62,183,186,215],ricoh_client_mod:[62,206],ricoh_theta:[174,206],ricoh_theta_image_servic:206,ridg:46,riff:64,right:[2,4,21,26,29,32,40,47,48,49,50,55,56,58,59,61,62,64,76,79,94,108,109,189,191,197,201,219,220,224],right_depth:61,right_depth_in_visual_fram:61,right_fisheye_imag:[55,58,61],right_fisheye_image_0000:55,right_fisheye_image_0001:55,right_fisheye_image_0004:55,right_fisheye_image_0009:56,right_fisheye_image_0027:56,right_fisheye_image_0076:55,right_fisheye_image_0077:55,right_hip_i:[2,29],right_hip_x:[2,29],right_kne:[2,29],rightsid:62,rigid:45,rise:[4,47,48,64],risk:[6,61,64],rj45:39,rle:[40,64,93],rle_count:64,robin:39,robot:[2,3,6,7,8,9,10,11,12,13,16,17,19,21,22,24,25,27,28,29,31,32,33,35,36,38,41,42,43,44,45,49,50,53,55,57,58,59,63,64,66,67,68,69,70,71,73,74,75,76,77,78,79,80,82,83,84,85,86,87,88,89,90,92,93,94,95,96,97,98,99,101,102,103,104,105,107,112,114,121,125,126,128,129,130,131,132,151,153,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,175,176,180,182,186,188,190,191,194,196,197,198,200,201,202,206,207,208,210,211,212,214,215,216,217,218,219,221],robot_clock:[73,151],robot_clock_skew_nsec:151,robot_command:[21,58,59,61,109,130],robot_command_async:109,robot_command_feedback:[58,109],robot_command_feedback_async:109,robot_command_id:[64,109],robot_command_pb2:[21,109],robot_command_servic:68,robot_hostnam:[76,210,218],robot_id:[24,61,110],robot_id_servic:68,robot_ip:[27,41,44,160,161,162,163,164,165,166,167,168,169,170,171,172,173,175,176,177,178,179,188,189,190,191,192,193,194,195,196,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,215,216,217,219,220,221,222,223,224],robot_kinemat:[31,64],robot_local_grid:64,robot_metr:64,robot_password:175,robot_power_st:64,robot_power_state_off:64,robot_power_state_on:64,robot_power_state_unknown:64,robot_rt_person_ewrt_vis:59,robot_rt_person_ewrt_vision_hat:59,robot_seri:64,robot_st:[21,58,59,111],robot_state_cli:[58,59],robot_state_miss:21,robot_state_pb2:111,robot_state_servic:68,robot_tim:[73,126],robot_time_range_from_datetim:126,robot_time_range_from_nanosecond:126,robot_timestamp:[82,98],robot_timestamp_from_loc:151,robot_timestamp_from_local_nsec:151,robot_timestamp_from_local_sec:[126,151],robot_us:175,robotcommand:[7,21,59,61,62,68,109],robotcommandbuild:[58,59,61,62,109],robotcommandcli:[58,61,105,109,169],robotcommandissueserror:67,robotcommandresponseerror:[105,108,109],robotcommandservic:[61,105,109],roboterror:[62,108],robotfaultederror:92,robotget:11,robothardwareconfigur:64,robotid:[23,62,68,108,173],robotidcli:[61,110],robotidcommand:75,robotidrequest:[24,61,173],robotidrespons:173,robotidservic:[61,110],robotimpairederror:92,robotiniti:11,robotlinkmodel:64,robotlosterror:92,robotmodel:75,robotnotlocalizedtorouteerror:92,robotsoftwarereleas:23,robotst:[39,62,68,111],robotstatecli:[58,61,105,111],robotstatecommand:75,robotstateerror:92,robotstateservic:[61,111],robotstuckerror:92,robottimeconvert:[126,151],robottimeconvertor:126,robust:[18,28,39,42,46,58,62,64],robustli:47,robutt:[2,29],role:[35,61,64,88],roll:[2,29,37,43,61,62,64,100,109,150,179,224],room:[38,145],root:[6,13,21,23,36,37,39,62,64,91,129,153,155,158],root_frame_nam:[6,64],root_tform_task:[6,64],rope:58,rot:[59,64,100],rotat:[2,4,6,26,29,37,38,55,58,59,61,62,64,100,101,191,197,202,219,224],rotate_180:55,rotate_90_clockwis:55,rotate_body_param:2,rotate_imag:[58,64],rotate_image_align_horizont:[58,64],rotate_image_align_with_bodi:64,rotate_image_no_rot:64,rotate_image_unknown:64,rotation_ewrt_fram:64,rotation_set:64,rotation_setting_absolut:64,rotation_setting_offset:64,rotation_setting_unknown:64,rotation_with_toler:64,roughli:[20,64],round:[28,39,64,125,126,216],round_trip_tim:[64,126],rout:[9,11,13,15,17,20,22,34,43,50,62,85,92,107,196,207],route_gen_param:64,route_param:[64,92],routeerror:92,routegenparam:92,routenavigationerror:92,routenotupdatingerror:92,router:64,routin:[25,26,27,28,62,64,125,217],row:[26,31,41,58,64,94],rpath:189,rpc:[13,14,15,16,19,20,23,28,32,33,34,39,40,41,53,61,62,64,67,71,74,76,77,78,79,80,81,85,88,89,92,98,103,108,111,112,113,116,126,129,153,176,182,206,207,215,221],rpc_error:74,rpc_interval_second:[85,88,95],rpc_logger:113,rpc_method:[61,76],rpc_timeout:98,rpc_timeout_sec:103,rpc_timeout_second:[85,88],rpcerror:[62,74,77,79,80,82,83,84,85,87,88,89,90,92,93,97,98,101,102,103,104,105,108,109,111,126,130,153],rssi:64,rubi:[2,64],rule:[16,55],run:[2,3,4,10,11,13,14,18,19,21,23,24,28,29,31,34,36,37,38,39,40,50,51,52,54,55,56,57,58,59,61,62,64,70,74,75,76,78,80,92,101,125,126,153,154,158,159,196,200,201,202,207,212,215,224],run_camera_calibr:125,run_on_clos:138,run_spot_check:125,run_until_interrupt:[34,129],runawai:64,runner:129,running_man_param:2,runningman:2,runtim:[4,62,64,75,221],runtime_var:64,runtimeerror:103,rx_axi:64,rxrx:64,rxry:64,rxrz:64,rxx:64,rxy:64,rxz:64,ry_axi:64,ryi:64,ryri:64,ryrx:64,ryrz:64,ryx:64,ryz:64,rz_axi:64,rzrx:64,rzry:64,rzrz:64,rzx:64,rzy:64,rzz:64,safe:[6,20,21,24,35,36,56,57,59,61,62,64,88,95,105,108,109,126,204,221],safe_pb_enum_to_str:158,safe_pb_type_to_str:158,safe_power_off:[61,105],safe_power_off_command:109,safe_power_off_feedback:64,safe_power_off_request:64,safe_stop:6,safepoweroff:64,safepoweroffcommand:40,safeti:[14,15,40,47,61,210],sagitt:29,sai:[28,35,37,61,62,64,153],sale:60,same:[6,16,19,20,21,23,26,27,28,29,31,32,34,35,36,37,38,39,40,41,43,45,48,55,56,58,59,60,61,62,64,77,79,82,92,94,95,109,160,174,176,191,197,198,204,206,207,221],sampl:[19,43,64,145,174],samplecommon:62,sat:60,satisfi:[24,57,64,81],save:[9,16,18,21,22,24,28,31,32,33,41,44,55,56,57,62,64,67,75,77,79,80,93,108,127,173,175,176,191,200,201,209,215,217],save_choreography_sequence_to_fil:67,save_images_as_fil:93,saved_model:[57,58,59],saw:57,scalar:64,scale:[2,17,19,58,64,116],scale_factor:64,scallop:47,scan:[19,41,43,64,107],scan_match_region:64,scenario:[28,187],scene:[18,40,62,64,130,219],schema:[33,64,82,158],schema_id:[64,82],schema_nam:[33,64,82],schema_sourc:33,schemat:48,scheme:[17,23,40],scope:[21,53,64,202],score:[57,64],scp:44,scratch:56,screen:[32,57,60,62,64,116,191,206,212,219],screenshot:[28,32,44],screw:49,script:[2,26,28,38,41,43,55,56,57,58,59,60,62,129,161,164,173,174,176,179,201,206,213,215],scroll:26,sculpt:45,sdk:[3,4,5,9,11,12,16,18,19,23,27,32,34,36,39,40,41,42,43,44,52,54,55,57,58,62,68,70,74,108,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,175,176,179,186,188,189,195,198,205,206,209,210,211,212,213,215,216,217,218,222,223,224],sdkerror:112,se2:[6,64,91],se2_fame_nam:109,se2_frame_nam:[64,109],se2_trajectory_feedback:[58,64],se2_trajectory_request:[6,64],se2_vel_vector:100,se2_velocity_feedback:64,se2_velocity_in_b:100,se2_velocity_request:64,se2pos:[91,92,100,109],se2trajectorycommand:[58,109],se2veloc:[59,91,100],se2velocitycommand:62,se2velocitylimit:[59,62,92,107],se3:[64,100],se3_vel_vector:100,se3_velocity_in_b:100,se3covari:62,se3pos:[20,37,59,62,91,92,100,107],se3trajectori:[6,109],se3trajectorypoint:62,se3veloc:[91,100],seal:45,search:[7,13,50,58,59,64],search_ray_end_in_fram:64,search_ray_start_in_fram:64,seat:[29,61],sec:[58,62,64,151],sec_to_nsec:151,second:[2,6,14,20,21,23,24,26,28,29,33,35,39,40,41,47,53,58,59,60,61,62,64,70,80,85,88,92,94,95,98,103,107,108,109,111,126,129,151,162,173,176,177,196,204,209,210,212],second_foot:[2,29],secondari:[40,64],seconds_to_dur:151,seconds_to_timestamp:151,secret:[41,43,44,64,103,108,176,206,207,208,221],secs_to_hm:151,section:[5,10,19,21,26,27,28,30,32,40,42,43,44,47,51,55,56,58,59,60,61,62,176,206,207,210,212,221,224],secur:[14,19,24,33,39,49,51,60,61,74],see:[0,5,6,7,13,16,20,21,23,26,28,32,35,38,50,51,55,56,57,58,59,61,62,64,71,85,86,88,98,105,108,109,110,112,158,160,165,171,173,174,175,176,177,178,179,180,181,182,183,185,186,187,188,189,190,192,194,195,196,198,199,200,201,204,205,206,207,208,209,211,213,216,217,218,220,221,222,223],seed:[20,62],seed_tform_bodi:[20,64],seek:[24,45,62,149],seekabl:137,seem:89,seen:[28,39,44,56,58,61,62,64,95,191,201,205,207],segment:[55,64],segreg:64,select:[6,7,28,29,33,39,44,48,50,55,57,58,59,60,62,64,97,116,137,161,165,173,176,204,206,221],selector:[21,28],selector_miss:21,self:[2,18,20,21,26,29,39,40,41,47,57,62,64,74,75,95,100,150,159,176,185,201,204,206,207,220,221,224],self_own:95,self_register_payload:207,self_register_servic:207,self_registr:62,selfright:[61,64],selfright_command:109,selfright_feedback:64,selfright_request:64,semant:[23,40,61,202],semi:7,send:[6,23,26,28,32,33,35,38,39,40,43,47,57,58,59,62,64,74,75,78,80,85,92,98,103,108,112,126,160,202,204,207,221],sender:43,sens:[0,41,42,64],sensit:[6,45,61,201],sensor:[0,3,6,8,16,17,18,19,20,21,23,32,33,34,37,40,41,52,54,58,62,64,94,176,196],sensori:13,sent:[2,6,26,31,32,35,39,40,41,43,58,64,82,88,108,109,129,173,207],separ:[28,31,32,41,53,58,60,62,64,120,127,160,161,162,163,164,165,166,167,168,169,170,171,172,175,190,198,212,217,221],sepcif:64,seper:64,seq:209,sequenc:[1,2,6,14,17,23,24,28,29,33,61,62,67,82,95,98,105,108,158,187],sequence_id:[64,82],sequenti:64,seri:[28,33,48,62,64,83,132,133,135,136,137,138,139,140,141,142,143,146,147,149,173,176,215],serial:[23,24,39,60,62,64,143,148,196,199],serial_numb:[23,61,64],serializ:82,serializetostr:41,series_block_file_offset:139,series_block_index:[64,137,139,149],series_block_index_offset:64,series_descriptor:[64,137,139,140,142,144,146,149],series_identifer_hash:64,series_identifi:[64,139],series_identifier_hash:64,series_identifier_to_hash:139,series_index:[64,137,138,139,140,141,142,147,149],series_index_to_descriptor:147,series_spec:[133,136,138,139,144,145,148],series_spec_to_index:133,series_typ:[24,64,135,136,138,139,140,142,145,148],seriesblockindex:[137,139,149],seriesdescriptor:[137,139,144,146,147,149],seriesidentif:64,seriesidentifi:[24,135,136,139],seriesnotuniqueerror:[136,138,139],seriou:76,serv:[64,85,127,207],server:[3,17,33,35,36,39,50,52,57,58,59,62,64,68,71,74,88,89,92,95,101,120,126,129,152,156,175],server_config:[58,64],server_data:58,server_error:64,server_ip:175,server_rx:64,server_tx:64,server_util:[62,113,157],servererror:89,servic:[1,2,3,5,9,11,13,15,16,18,19,20,24,25,27,30,31,37,38,39,42,44,51,52,53,54,57,58,59,63,67,68,69,71,73,74,75,76,77,78,79,81,82,84,85,86,87,88,89,90,91,92,93,95,97,98,99,101,102,103,104,105,107,108,109,110,111,112,113,115,116,117,118,119,120,121,122,123,124,125,126,129,130,132,135,152,153,154,156,159,173,174,180,181,183,184,186,187,189,194,199,202,204,217,222],service_author:34,service_cli:112,service_entri:64,service_error:64,service_fault:[90,208],service_fault_id:90,service_fault_pb2:94,service_fault_st:64,service_ip:34,service_nam:[21,58,64,84,94,108,112,135,142,143,202,215],service_port:34,service_read:140,service_runn:34,service_servic:[34,129],service_typ:[34,36,67,69,71,77,79,80,81,82,83,84,85,86,87,88,90,92,93,95,96,97,98,99,101,102,103,104,105,107,108,109,110,111,112,115,116,117,118,119,120,121,122,123,124,125,126,130,153,156],servicealreadyexistserror:85,servicedoesnotexisterror:85,serviceentri:[23,43],servicefailedduringexecutionerror:89,servicefault:[90,94],servicefaultalreadyexistserror:90,servicefaultdoesnotexisterror:90,servicefaultid:90,servicefaultst:62,serviceservic:129,serviceunavailableerror:89,serviv:51,session:[21,31,32,39,51,64,156],session_id:[64,156],set:[2,6,10,14,15,18,19,21,23,24,26,28,29,31,32,33,35,36,38,39,40,41,43,44,51,53,55,56,57,58,59,60,61,62,64,70,73,74,78,80,82,88,93,94,95,98,100,101,103,106,107,108,109,112,115,116,118,119,120,122,123,127,129,138,139,151,153,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,174,189,196,198,204,206,209,210,212,218],set_challeng:88,set_client_nam:95,set_colormap:209,set_complete_if_no_error:80,set_config:88,set_config_async:88,set_ice_configur:120,set_ice_configuration_async:120,set_ir_colormap:116,set_ir_colormap_async:116,set_ir_meter_overlai:116,set_ir_meter_overlay_async:116,set_last_captured_imag:94,set_led_bright:118,set_led_brightness_async:118,set_loc:92,set_localization_async:92,set_localization_request:64,set_logg:94,set_max_message_length:112,set_passphras:119,set_passphrase_async:119,set_posit:209,set_power_statu:121,set_power_status_async:121,set_ptz_posit:122,set_ptz_position_async:122,set_ptz_veloc:122,set_ptz_velocity_async:122,set_recording_environ:107,set_recording_environment_async:107,set_response_head:157,set_screen:116,set_screen_async:116,set_statu:[41,80],set_stream_param:123,set_stream_params_async:123,set_timestamp_from_datetim:151,set_timestamp_from_now:151,set_timestamp_from_nsec:151,set_volum:[115,209],set_volume_async:115,setaudiocapturechannel:64,setaudiocapturegain:64,setblackboard:21,setestopconfig:64,setestopconfigrequest:35,seticeconfigur:64,setircolormap:[62,64],setirmeteroverlai:[62,64],setledbright:64,setloc:[17,19,64],setlocalizationrequest:62,setlocalizationrespons:62,setpassphras:64,setposit:64,setpowerstatu:[62,64],setptzposit:64,setptzveloc:64,setrecordingenviron:[17,64],setscreen:64,setstreamparam:64,settl:64,settle_then_cut:[35,40,88],settle_then_cut_async:88,setup:[25,26,51,55,56,58,129,174,206,212,221,224],setup_log:129,setup_token_cach:108,setvolum:64,seven:26,sever:[23,26,32,35,40,61,62,109,206,214],severity_crit:64,severity_info:64,severity_scor:64,severity_unknown:64,severity_warn:64,sfixed32:[2,64],sfixed64:[2,64],sh0:[29,64],sh1:64,sha1:[24,64],shade:64,shader:[62,214],shadow:[19,64],shall:53,shape:[29,45,57,64],share:[21,23,40,64,95,133,174,189,196,197],sharp:29,sheet:19,shelf:[55,59],shell:[48,173],shield:46,shift:122,shift_pan_angl:122,shine:211,ship:[23,34,50],shock:[64,119],shoe:58,shoould:64,shore:[61,64],shore_power_st:64,shore_power_state_off:64,shore_power_state_on:64,shore_power_state_unknown:64,shorepowerconnectederror:105,shortest:64,shorthand:61,shot:[55,58,61,64],should:[2,6,13,14,15,16,18,19,20,23,24,26,28,29,33,34,35,36,37,38,39,40,41,43,44,45,46,47,48,49,50,51,53,55,56,57,58,59,60,61,62,64,76,79,80,82,85,88,89,92,94,95,98,101,103,105,108,109,112,113,121,125,126,153,160,165,171,174,175,177,191,197,198,201,203,204,206,207,209,211,212,215,221],should_exit:126,shoulder:[29,64],shoulder_0:2,shoulder_1:2,shouldn:[64,107],show:[2,4,7,16,21,24,25,26,34,39,40,41,43,44,56,58,60,61,62,64,75,92,107,129,160,165,169,171,173,174,176,180,182,183,184,185,186,187,190,191,197,203,204,205,206,208,212,214,217,219,221],show_progress:73,shown:[16,21,23,26,28,32,33,34,37,39,44,45,47,48,56,59,62,64,196,210,212,219],shrunk:26,shut:[95,126,129,204],shutdown:[64,85,88,95,103,129,204],shutter:64,side:[18,29,40,44,45,46,62,64,109,160,161,162,163,164,165,166,167,168,169,170,171,172,198],side_left:2,side_param:2,side_right:2,side_unknown:2,sidewalk:[7,62,164],sight:201,sigint:[34,129,221],sign:[2,23,29,40,60,64,204],signal:[48,64,82],signalschema:82,signaltick:82,signatur:[26,41,80],signfic:64,signific:[39,47,62,64],significantli:[18,20,23,40,64],silent:62,similar:[7,19,26,29,32,40,50,61,62,64,95,100,164,188,221],similarli:[6,37,62,64],simpl:[6,24,33,51,54,55,60,62,64,164,170,180,207,221],simpleparallel:21,simpler:[39,190],simplest:[15,35,61,109],simpli:[14,26,60,64,174],simplic:61,simplifi:[23,32,33,36,39,41,53,62,80,180,187,196],simul:164,simultan:[28,64,160],sinc:[2,6,20,23,29,37,39,40,50,54,56,57,59,61,62,64,76,105,108,109,112,126,129,138,145,148,151,173,176,177,190,191,204,206,210,221],singl:[6,13,19,21,23,26,28,29,31,33,35,36,37,39,49,55,61,62,64,80,82,86,88,91,94,95,100,112,138,146,207,223],sint32:[2,64],sint64:[2,64],sip:55,sit:[2,4,10,14,21,26,28,35,40,47,48,61,62,64,88,98,105,109,125,160,161,162,163,164,165,166,167,168,169,170,171,172,179,196,198,201,211,213,220,224],sit_command:[62,109],sit_feedback:64,sit_miss:21,sit_request:[21,64],sitcommand:21,site:[0,3,17,20,42,52,54,61,62,189,218],situat:[6,20,26,61,62,64],six:[23,49],size:[2,15,19,24,29,40,55,62,64,89,100,107,138,189,196,210,212],skeleton:[40,111],skew:[61,64,100,126,151,216],skew_matrix_2d:100,skew_matrix_3d:100,skink:196,skip:[7,28,62,189,212],skip_app_token_check:108,sky:64,sleep:[21,55,58,59,62,206,210,212],slew:62,slew_rate_limit:64,slice:[2,26,29],slices_per_minut:2,slide:29,slight:56,slightli:[6,24,64],slip:20,slope:[4,64],slot:49,slow:[28,41,56,64,206,221],slower:[28,29,41,64],slowest:29,slowli:[23,29],small:[8,17,19,20,28,39,46,57,58,64,100,219],smaller:[26,29,58,62],smallest:64,smi:56,smooth:[2,18],smoother:29,snapshot:[13,16,17,18,37,40,41,62,64,91,92,94,108,196,197,223],snapshot_:196,snapshot_id:64,snippet:[4,21,36,43,174],snow:20,soccer:37,socket:39,soft:[48,62,64],softwar:[0,9,23,24,35,36,39,40,42,44,45,47,60,62,64,71,74,88,124,160,161,162,163,164,165,166,167,168,169,170,171,172,176,185,189,190,198,201,206,207,208,209,210,217,221],software_correct:64,software_releas:[23,61,64],softwarevers:124,solder:45,sole:61,solid:206,solut:[9,44,60,61,64,214],some:[2,6,7,8,16,18,21,23,24,26,28,29,35,36,39,40,42,44,54,55,56,57,58,59,60,61,62,64,70,80,108,158,159,160,169,204,210],some_image_channel:31,somehow:64,someth:[6,15,21,28,29,55,57,60,62,64,84,85,90,103,105,204],sometim:[6,26,60,62,64,206],somewher:[6,92],song:[2,26,29],soon:[26,64],sooner:[62,64],sort:[24,26,28,39,62,64,92],sound:[62,115],soundrequest:64,sourc:[7,18,31,32,33,40,41,55,56,57,58,60,61,62,64,67,69,70,71,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,153,154,155,156,157,158,165,171,189,191,202,206,210,215,219,221],source1:[41,191],source2:[41,191],source_fil:174,source_nam:94,sourcedataerror:[93,104],sourcenam:93,space:[6,7,16,19,20,26,28,33,37,45,47,49,53,58,62,64,109,122,160,161,165,171,188,197,201,213,220],spacebar:201,spam:36,span:[19,24],spars:41,spawn:23,speak:[0,39,61],speaker:[62,64],spec:[24,64,126,133,138,139],speci:[23,61,64],special:[7,19,25,27,33,35,37,47,62,64],special_data:41,special_fram:37,specialfram:37,specif:[2,5,11,13,16,18,26,28,29,32,33,34,36,37,39,40,41,42,43,44,50,53,56,60,61,62,64,73,74,76,87,92,94,95,99,100,101,109,111,112,130,176,177,182,195,196,198,207,215,216,221],specifc:64,specifi:[2,6,7,15,16,19,21,23,24,26,28,29,32,33,35,39,40,41,44,51,53,58,61,64,68,73,74,75,78,80,81,84,85,88,90,92,93,94,95,97,98,104,109,111,115,119,122,126,129,141,142,146,152,153,165,171,175,176,177,189,191,193,196,202,204,206,210,212,213,215,219,221,224],speed:[2,4,8,13,14,15,20,21,29,41,44,59,62,64,92,107,189],speed_multipli:[2,29],speed_vari:[2,29],spell:60,spend:[29,55],spent:[2,29],sphere:[64,219],spheric:64,spike:48,spin:129,spiral:29,splash:45,split:[2,29,56,58,62,64,138],split_dataset:56,split_fract:[2,29],sport:210,spot:[1,7,9,10,11,12,16,18,19,20,21,23,24,26,27,28,29,31,32,33,34,36,38,39,40,43,45,46,47,48,52,53,54,56,57,58,59,63,67,68,74,87,103,108,109,115,116,117,118,119,120,121,122,123,124,159,173,175,176,179,181,182,184,186,187,188,189,190,191,195,196,199,201,202,203,204,205,208,211,213,215,216,217,218,219,220,221,222,223,224],spot_cam:[62,68,115,116,117,118,119,120,121,122,123,124],spot_check:125,spot_check_cli:125,spot_check_command:125,spot_check_command_async:125,spot_check_feedback:125,spot_check_feedback_async:125,spot_check_servic:68,spot_command_pb2:109,spot_detect_and_follow:210,spot_light:211,spot_tensorflow_detector:[210,212],spot_v2_0:61,spotbblank02:61,spotcam:[31,64,68],spotcamstoremedia:21,spotcheck:[33,62,64,68,125],spotcheckcameratimeouterror:125,spotcheckcli:125,spotcheckcommand:64,spotcheckcommandrespons:62,spotcheckendstoptimeouterror:125,spotcheckerror:125,spotcheckfeedback:64,spotcheckfeedbackrespons:[62,125],spotcheckgroundcheckerror:125,spotcheckimucheckerror:125,spotcheckloadcelltimeouterror:125,spotchecknotsittingerror:125,spotcheckpoweronfailur:125,spotcheckresponseerror:125,spotcheckservic:[62,125],spotcheckstandfailureerror:125,spotchecktimedouterror:125,spotcheckunexpectedpowerchangeerror:125,spotcor:[42,44,50,210],spotdock:64,spotfetchcli:58,spotti:39,sprawl:[26,28,29],spread:[2,29],spun:34,squar:[16,19,160,197,219],squeez:[7,64],squeeze_grasp:64,src:[61,221],ssd:[56,210],ssd_resnet50_v1_fpn_640x640_coco17_tpu:[56,59],ssh:[44,50,51,206],ssid:206,ssl:[64,112,120],stabil:[60,62],stabl:[37,51],stack:[3,23,39],staff:64,stage:[7,64],stai:[8,17,18,37,47,59,64],stair:[4,13,16,18,40,47,62,109,224],stair_hint:[64,109],staircas:[18,64,210],stairs_tform_landing_cent:64,stairtrack:64,stairwai:4,stairwel:20,stale:[62,64,95],stall:[53,61,95,109],stamp:[62,195],stanc:[29,62,109,187],stance_command:109,stance_feedback:64,stance_in_plac:213,stance_length:[2,29],stance_request:64,stance_width:[2,29],stand:[2,4,6,14,21,26,28,29,34,40,47,48,57,58,59,61,62,64,109,125,162,163,166,167,168,169,170,179,189,196,198,201,204,211,213,220,224],stand_command:[61,62,109],stand_feedback:64,stand_miss:21,stand_request:[21,64],stand_tim:[2,29],standalon:51,standard:[1,4,10,21,29,34,39,41,42,43,45,50,51,55,58,61,62,63,206,208,221],standbi:4,standcommand:21,stare:64,start:[2,11,13,15,17,19,20,21,23,24,26,27,28,29,31,32,33,36,39,41,44,48,54,56,57,58,59,60,61,62,64,73,78,85,92,94,103,107,108,109,119,125,126,129,133,134,159,176,177,196,201,205,206,210,211,212,215,224],start_captur:94,start_counter_state_nam:64,start_datetim:126,start_level:158,start_nsec:[73,126],start_record:107,start_recording_async:107,start_slic:2,start_tim:[2,58,64,73],start_time_sec:78,start_time_sync:108,start_timestamp:64,starter:62,starting_angl:[2,29],starting_veloc:64,startrecord:[17,64],startrecordingrespons:62,startrequest:64,startup:[23,32,62,64],stat:64,state:[2,4,6,13,17,18,20,21,23,28,36,39,41,43,56,58,59,63,68,75,80,85,86,89,90,92,94,95,103,105,107,108,109,119,121,126,153,159,181,182,186,188,200,206,219],state_body_pos:64,state_camera_check:64,state_cli:[61,105,111],state_descript:64,state_endstop_c:64,state_error:64,state_estop:64,state_finish:64,state_hip_range_of_motion_check:64,state_loadcell_c:64,state_nam:[21,64],state_not_estop:64,state_off:64,state_off_shore_pow:64,state_on:64,state_on_shore_pow:64,state_powering_off:64,state_powering_on:64,state_reverting_c:64,state_start:64,state_unknown:64,state_unknown_shore_pow:64,state_user_abort:64,state_waiting_for_command:64,statement:[44,62,64,113],static_ip:206,station:[14,21,62,64,86],station_id:86,stationari:[29,189],statu:[14,17,18,20,21,32,33,35,36,39,40,41,50,51,53,57,58,61,62,75,76,77,78,79,80,85,86,88,89,92,95,97,105,107,108,109,119,125,129,188,204,206,209,215,221,224],status:[53,64,80,94],status_abort:64,status_acquir:64,status_acquisition_cancel:64,status_already_answ:64,status_already_exist:64,status_ambigu:64,status_applying_forc:64,status_at_go:[58,64],status_at_prep_pos:[14,62,64],status_battery_miss:64,status_behavior_fault:[62,64],status_boot:64,status_calibration_error:64,status_camera_focus_error:64,status_cancel_acquisition_fail:64,status_cancel_in_progress:64,status_charg:64,status_clear:[64,109],status_command_in_progress:64,status_command_overridden:[15,64],status_command_timed_out:[15,64],status_compile_error:64,status_complet:64,status_config_mismatch:64,status_constraint_fault:[15,20,64],status_could_not_create_waypoint:64,status_could_not_update_rout:64,status_data_error:64,status_data_invalid:64,status_data_unavail:64,status_delet:64,status_deletion_fail:64,status_discharg:64,status_dock:[14,64],status_does_not_exist:64,status_drag:64,status_endpoint_mismatch:64,status_endpoint_unknown:64,status_error:64,status_error_command_timed_out:64,status_error_dock_lost:64,status_error_dock_not_found:64,status_error_leas:64,status_error_no_timesync:64,status_error_not_dock:[62,64],status_error_system:64,status_error_too_dist:64,status_estop:64,status_exist:64,status_expir:64,status_expired_application_token:[39,64],status_fail:64,status_failed_to_cancel:64,status_failur:64,status_fault:64,status_fault_already_act:64,status_fault_not_act:64,status_feature_desert:64,status_fiducial_pose_not_ok:64,status_fiducial_pose_uncertain:64,status_fiducial_too_far_awai:64,status_fiducial_too_old:64,status_following_rout:[15,64],status_getstatus_error:64,status_going_to_go:64,status_going_to_st:64,status_grasp_fail:64,status_high_error:64,status_image_data_error:[62,64],status_in_progress:[14,64],status_incompatible_hardwar:64,status_incorrect_challenge_respons:64,status_internal_error:[41,64],status_invalid:88,status_invalid_application_token:[39,64],status_invalid_cod:64,status_invalid_credenti:64,status_invalid_edg:64,status_invalid_endpoint:64,status_invalid_id:64,status_invalid_leas:64,status_invalid_login:[39,64],status_invalid_mutation_id:64,status_invalid_question_id:64,status_invalid_request:64,status_invalid_resourc:64,status_invalid_session_id:64,status_invalid_token:[39,64],status_invalid_uploaded_choreographi:2,status_is_sit:64,status_is_stand:64,status_lease_error:[2,15,64],status_license_error:64,status_lost:[15,20,64],status_malform:64,status_map_too_large_licens:64,status_misalign:64,status_miss:64,status_missing_fiduci:[62,64],status_missing_input:64,status_missing_leas:64,status_missing_transform:64,status_more_samples_need:64,status_motors_on:64,status_near_go:[62,64],status_never_run:64,status_no_licens:64,status_no_loc:[15,64],status_no_matching_fiduci:64,status_no_miss:64,status_no_mission_plai:64,status_no_path:64,status_no_permiss:64,status_no_rout:[15,64],status_no_such_grid:64,status_no_timesync:64,status_non:64,status_nonexistent_servic:64,status_not_active_leas:64,status_not_authoritative_servic:64,status_not_clear:64,status_not_found:64,status_not_localized_to_end:64,status_not_localized_to_existing_map:64,status_not_localized_to_map:64,status_not_localized_to_rout:[15,64],status_not_manag:95,status_not_powered_on:64,status_not_ready_yet:64,status_not_record:64,status_not_yet_valid:64,status_ok:[2,39,64],status_old:64,status_old_docking_command:64,status_other_failur:64,status_other_own:95,status_paus:64,status_payload_not_author:64,status_point_cloud_data_error:64,status_power_error:64,status_powered_off:64,status_process:[64,109],status_queu:62,status_reached_go:[15,64],status_record:64,status_remote_cloud_failure_no_data:64,status_remote_cloud_failure_not_in_directori:64,status_request_error:64,status_request_id_does_not_exist:64,status_resource_already_claim:64,status_revok:[64,95],status_robot_command_error:64,status_robot_command_issu:2,status_robot_fault:15,status_robot_frozen:[15,64],status_robot_impair:64,status_run:[62,64],status_sav:[41,64,80],status_self_own:95,status_serial_mismatch:64,status_service_not_readi:64,status_shore_power_connect:64,status_small_mass:64,status_snapshot_does_not_exist:64,status_source_data_error:64,status_stalled_holding_item:64,status_stanc:64,status_stuck:[15,62,64],status_success:[64,204],status_target_not_cent:64,status_target_not_gravity_align:64,status_target_not_in_view:64,status_target_upside_down:64,status_temporarily_locked_out:[23,39,64],status_timedout:[41,64],status_to_error:76,status_to_str:76,status_too_dist:64,status_too_far_awai:64,status_tool_trajectory_stal:64,status_trajectory_cancel:64,status_trajectory_complet:64,status_trajectory_stal:64,status_unclear:64,status_unknown:[2,39,53,64],status_unknown_camera:64,status_unknown_capture_typ:64,status_unknown_fram:64,status_unknown_route_el:64,status_unknown_sourc:64,status_unknown_waypoint:64,status_unmanag:64,status_unown:95,status_unrecognized_command:[62,64],status_unsupport:64,status_unsupported_image_format_request:[62,64],status_user_cancel:64,status_valid:64,status_validate_error:64,status_warn:64,status_wrong_epoch:64,statuscod:61,statustyp:76,stdin:60,stdio:189,stdout:58,steadi:[29,64,211],steer:29,step:[2,4,16,26,28,35,40,41,44,47,50,51,54,56,59,61,64,155,207,219],step_param:2,stereo:[4,62],stick:[6,21,164,224],sticker:60,stiff:64,stiffer:64,still:[6,15,26,36,41,43,50,56,58,59,61,62,64,85,98,103,207],stillimag:64,stitch:[62,64,186,206,209],stitch_front_imag:214,stood:60,stop:[3,10,15,20,21,22,26,29,40,41,44,51,55,57,59,62,64,68,85,88,94,95,103,105,107,108,109,126,128,129,153,156,160,161,162,163,164,165,166,167,168,169,170,171,172,179,182,196,198,201,204,205,206,210,213,220,224],stop_async:[88,156],stop_captur:94,stop_command:109,stop_feedback:64,stop_level:[40,61,64,88],stop_level_detail:[61,64],stop_record:107,stop_recording_async:107,stop_request:64,stop_time_sync:108,stoplevel:88,stoprecord:[17,64],storag:[33,62,64,80,95,127,174,177],store:[2,13,16,17,18,22,24,30,31,32,33,37,40,41,43,55,58,62,63,64,68,78,79,80,86,94,119,136,138,139,141,143,147,148,155,173,174,177,197,207,209,215],store_async:119,store_cli:80,store_data:[41,80,81],store_data_async:81,store_help:[41,80],store_imag:[80,81],store_image_async:81,store_metadata:[41,80,81],store_metadata_async:81,store_retriev:209,store_tru:57,stored_pb_typ:155,storedata:64,storedatarespons:81,storeimag:64,storeimagerespons:81,storemetadata:64,storemetadatarespons:81,stow:[6,58,59,62,64,180],stow_cmd:[58,59],stow_stat:64,stowstate_deploi:64,stowstate_stow:64,stowstate_unknown:64,str:[2,57,64,129],strafe:[26,201,220],straight:[4,58,59,64,164],straight_staircas:64,strategi:39,stream:[3,16,17,24,33,39,62,76,92,114,116,121,123,129,132,133,221],stream_data_read:149,stream_file_index:149,streamabl:24,streamdataread:149,streameddataread:133,streaming_common_header_error:76,streaming_common_lease_error:76,streamlin:[40,62,85],streamqual:63,streamqualitycli:123,streamqualityservic:[62,123],street:21,stretch:[26,29],strict:[62,64],stricter:10,strictli:59,strictvers:110,strike:198,string:[2,21,23,24,32,37,39,41,44,53,61,62,64,71,73,76,77,78,80,81,82,91,92,93,94,95,97,103,104,107,108,109,111,112,119,126,138,139,145,148,151,158,176,204,206,207,221],string_messag:207,string_valu:64,stringifi:158,strip:[33,39,64],strip_get_image_respons:129,strip_image_respons:129,strip_large_bytes_field:129,strip_local_grid_respons:129,strip_log_annot:129,strip_record_data_blob:129,strip_record_signal_tick:129,strip_store_data_request:129,strip_store_image_request:129,strive:64,strong:39,strongli:76,struct:64,struct_typ:64,structur:[9,17,28,33,37,39,41,45,55,57,59,61,62,64,77,79,91,92,117,134,136],stub:[71,110],stub_creation_func:76,stuck:[15,17,62,64,92],studi:47,stuff:55,stun:64,style:[2,29,33,52,53,64,65,218],sub:[2,39,62,64,74,95],sub_leas:95,subclass:[61,62,76,92,94,98,108],subcommand:[64,75],subdirectori:53,subfield:64,subfold:196,subject:64,subleas:95,submessag:[24,64],subnet:64,subnet_mask:206,subpars:75,subsequ:[18,28,36,40,62,64,95,153],subset:[6,32,33,64,210,212],substr:62,subtract:[59,64],subtre:[21,64],succe:[20,21,28,58,64,94,204],succeed:[39,58,64,89,105,108],succeeed:64,succes:50,success:[13,15,17,21,23,39,57,58,59,60,62,64,77,79,80,94,103,105,109,125,154,158,174,204],successexitstatu:51,successfulli:[18,21,40,55,58,59,60,61,62,64,78,80,94,101,109,126,164,191,206,215,221],suddenli:10,sudo:[27,44,50,51,175,176,210,218,221,224],suffici:[20,58,59,60,64],suffix:[53,93],suggest:[44,64,107,206,221],suit:19,summar:64,summari:[9,64],super_leas:95,supervis:[10,61],supervisor:21,suppli:[23,48,60,64,92,109,196],support:[0,5,6,9,11,18,23,24,25,26,27,28,39,42,44,47,48,49,50,53,57,60,61,64,68,71,97,109,152,173,176,189,210,212,224],suppos:2,suppress:[64,85],suppress_incorrect:88,sure:[19,26,41,44,53,55,57,58,59,60,61,62,64,80,160,161,162,163,164,165,166,167,168,169,170,171,172,198,201,206],surfac:[5,7,45,49,61,62,63,64,68,180],surround:[40,45,62],surviv:45,suspected_ambigu:64,sustain:45,swai:2,sway_param:2,sway_style_fast_out:2,sway_style_fast_return:2,sway_style_plateau:2,sway_style_spik:2,sway_style_squar:2,sway_style_standard:[2,29],sway_style_unknown:2,swing:[2,29,47,64],swing_direct:64,swing_direction_insw:64,swing_direction_outsw:64,swing_direction_unknown:64,swing_height:[2,29,64],swing_height_high:64,swing_height_low:64,swing_height_medium:64,swing_height_unknown:64,swing_slic:[2,29],swing_veloc:[2,29],swing_waypoint:[2,29],symbol:[39,64],symmetr:[64,100],symmetri:64,sync:[2,8,26,53,58,61,62,63,64,68,75,86,92,108,182,201,216,220],sync_with_directori:[55,62,108],synchro:[62,109],synchro_se2_trajectory_command:109,synchro_se2_trajectory_point_command:[62,109],synchro_sit_command:[62,109],synchro_stand_command:[62,109],synchro_trajectory_command_in_body_fram:109,synchro_velocity_command:[62,109],synchron:[2,5,23,25,27,29,40,47,48,59,62,63,64,89,98,109,126],synchronized_feedback:64,synchronizedcommand:[62,109],syntax:[64,91,173],synthet:64,sys:[55,57,58],system:[6,10,14,16,18,20,21,23,29,33,39,40,41,42,43,44,45,47,48,51,53,56,57,58,61,62,64,85,88,89,92,93,95,98,104,109,117,119,126,129,136,151,186,188,189,197,207,208,218,221],system_fault_st:64,system_stat:119,systemctl:[50,51],systemd:[51,57],systemfaultst:40,tab:[44,51,56,201,220,221],tabl:[26,50,64,68,152,159],tablet:[3,9,10,13,17,18,20,22,32,36,38,40,41,42,43,50,55,57,58,59,61,62,64,176,179,191,196,201,204,205,206,210,213,214,215,217,221],tag36h11:19,tag:[14,31,37,64,115,119,189,197],tag_async:119,tag_id:64,take:[2,6,10,15,17,19,20,21,23,26,28,29,32,35,36,39,40,41,44,50,51,53,55,56,57,58,59,62,64,75,76,86,95,98,109,151,165,171,174,176,202,204,206,208,210,212,214,219,220,221],take_async:95,takeleas:[64,95],taken:[6,16,17,22,41,45,58,60,61,64,70,95,197],talk:[35,39,55,62,64,76,160,162,163,165,166,167,168,169,170,171,198,204],tall:[46,64,198],tap:29,tape:[19,164],tar:[44,50,56,176],target:[6,19,43,45,51,53,57,59,60,62,64,88,107,189,204],target_bitr:123,target_config_id:[64,88],target_endpoint:[35,64],target_nam:155,target_pb_typ:155,target_trajectory_in_frame1:64,target_trajectory_initial_veloc:64,targetbitr:64,task:[6,7,12,21,50,64,68,99],task_t_tool:64,tcp:43,teach:59,team:50,teardown_sess:156,teardown_session_async:156,teardownsess:[21,64,204],technic:9,techniqu:[19,53,58,109],teleop:[31,32,41,64],teleop_2020:31,teleop_:31,teleoper:31,tell:[2,10,17,21,56,58,59,62,64,92,109,112,134,190,207],telop:64,temp:[8,64,119],temperatur:[4,62,116,117,119],tempo:29,temporari:62,temporarili:[39,62,64,71],temporarilylockedouterror:71,tempt:80,tenni:38,tensor:[57,64],tensorboard:56,tensorflow:[56,57,59,62,159,186,202,210],tensorflow_object_detector:210,tensorflow_serv:[57,202],tensorflowobjectdetectionmodel:57,term:[25,39,53,61,64],termin:[41,51,56,57,58,60,61,89,196,201,204,220],terminolog:217,terrain:[16,40,62,64,107,197,219],terrain_param:64,terrainparam:62,test:[26,39,50,53,55,56,57,58,59,62,64,74,95,158,164,182,183,191,204,210,221,224],test_driv:206,test_payload:43,test_util:158,tester:[41,62,176,183,206,221],tester_program:[41,62],text:[2,24,26,28,43,53,57,59,61,64,75,82,98,108,177,199,201,204,217,219],text_messag:[64,82,98],textmessag:82,textmsg:[62,75],textmsgcommand:75,textual:2,textur:18,tf1_detection_zoo:202,tf2:56,tf_force_gpu_allow_growth:56,tform:[37,62,64,100],than:[0,2,6,10,20,21,23,26,28,29,31,35,39,40,41,49,50,51,53,54,56,58,60,61,62,64,71,80,89,95,103,109,191,201,206,219],thank:62,thei:[3,6,10,13,14,16,18,19,21,23,26,28,29,32,33,35,36,37,38,39,40,41,43,56,59,60,61,62,64,91,95,100,174,176,191,196,197,207,221],them:[13,16,19,21,26,31,36,40,41,44,53,56,60,61,62,64,75,80,84,92,145,173,196,212,214,218],theme:218,themselv:[10,35,43,56,62,207],therefor:[9,41,48,61,64,221],thermal:62,thermomet:[64,117],theta:[34,41,44,62,64,100,183,186,215],theta_ssid:206,thetayl00196843:206,thi:[0,2,4,6,7,8,9,10,11,14,15,16,17,18,19,20,21,23,24,26,28,29,30,31,32,33,35,36,37,38,39,40,41,42,43,44,45,46,47,48,50,51,53,54,55,56,57,58,59,60,61,62,64,65,70,74,75,76,77,79,80,81,82,85,86,88,89,92,93,94,95,98,100,102,103,104,105,107,108,109,112,116,120,125,126,129,130,138,139,141,142,144,145,146,147,148,151,153,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,188,189,190,191,192,193,194,196,197,198,199,200,201,202,203,204,205,206,207,208,210,211,212,213,214,215,216,217,218,219,220,221,223,224],thick:26,thin:53,thing:[6,10,41,55,56,57,58,64,95,100],think:[23,58,64],third:[34,36,50,60,61,106],this_model:57,those:[3,6,18,23,26,28,29,31,32,39,55,58,60,62,64,80,210,212,224],though:[16,155],thought:21,thread:[23,57,61,62,80,85,88,94,95,98,103,108,126,129,206,210],thread_except:126,thread_input_queu:57,thread_output_queu:57,threadpoolexecutor:[57,80],three:[6,24,26,28,32,39,59,64,109,210,212],threshold:[10,21,33,64,92,210,212],threshold_radian:[58,64],threw:[55,60,101],through:[6,7,10,18,19,24,26,29,32,33,34,36,39,40,41,44,48,50,51,57,59,60,62,64,79,80,85,90,92,126,129,159,161,176,186,198,206,207,215,218,221,224],throughout:[28,39,40,62,64],thrown:[28,62,80,89,108],thu:[24,64],thumb:55,thunk:138,thursdai:33,tick:[13,21,62,64,82,156],tick_async:156,tick_count:64,tick_data:64,tick_start_timestamp:64,tick_statu:156,tidi:98,tied:[45,64],tier:[2,29],tier_vari:[2,29],tight:6,tightli:[7,45],tile:64,tilt:[21,64,122,211],tilt_limit:64,time:[2,4,6,10,13,14,16,17,18,19,20,21,24,26,28,29,32,33,34,36,37,39,40,41,43,44,47,48,50,53,55,56,57,58,59,60,61,62,63,64,67,68,70,73,75,78,80,82,86,88,89,92,93,94,95,98,104,105,108,109,111,112,113,125,130,151,153,160,176,182,195,197,201,206,207,209,210,212,216,219,220,224],time_format_desc:151,time_nsec:151,time_rang:83,time_sec:108,time_since_refer:64,time_since_valid_respons:[61,64],time_spec:126,time_start:58,time_start_point:[67,130],time_sync:[23,55,58,61,108,126],time_sync_cli:[126,216],time_sync_endpoint:[98,126],time_sync_interval_sec:[108,126],time_sync_servic:68,time_sync_service_not_ready_interval_sec:126,timebas:64,timedouterror:[89,126],timeerror:92,timelin:[26,28,33,64],timeout:[10,15,32,35,36,39,40,41,58,61,62,64,80,85,86,88,92,94,98,103,108,109,111,126,207,221],timeout_deadlin:[41,64],timeout_sec:[58,59,61,94,105,108,109,125,126,129],timer:64,timerang:126,timespan:[64,73,126,151,173,177],timespan_spec:[73,126,151],timespec_to_robot_timespan:126,timestamp:[2,23,24,31,32,33,37,39,40,53,61,62,64,67,77,79,82,92,94,98,106,108,126,128,130,138,139,145,148,151,176,177,215],timestamp_cli:64,timestamp_filt:64,timestamp_nsec:[24,137,138,139,141,144,145,146,147,148],timestamp_proto:151,timestamp_sec:108,timestamp_str:151,timestamp_to_datetim:151,timestamp_to_nsec:151,timestamp_to_sec:151,timesync:[53,62,64,67,68,78,86,92,109,126,130,153],timesync_endpoint:[67,92,109,130,153],timesync_timeout_sec:126,timesynccli:126,timesynccommand:75,timesyncendpoint:[98,126,216],timesyncerror:126,timesyncrequir:[62,89],timesyncrespons:126,timesyncroundtrip:126,timesyncservic:126,timesyncthread:[23,126],timesyncupd:[23,64],timezon:176,tip:[7,8,10,55,56,60,64],tippi:29,titl:21,tls1:39,to_adjoint_matrix:100,to_axis_angl:100,to_euler_zxi:150,to_matrix:100,to_nsec:33,to_obj:100,to_pitch:100,to_proto:[88,100],to_quaternion:150,to_rol:100,to_rot_matrix:100,to_sec:33,to_timestamp:64,to_vector:100,to_waypoint:[64,107],to_waypoint_id:107,to_yaw:[59,100],todai:18,tof:8,togeth:[17,29,32,37,48,59,62,64,206],toggl:[62,189],toi:[55,56,57,58,59],token:[23,33,39,43,53,57,61,62,64,68,71,74,78,85,89,103,108,112,129,207],token_cach:[108,127],token_cb:74,token_manag:128,tokencach:127,tokencacheerror:127,tokencachefilesystem:127,tokenmanag:128,toler:58,tolerance_default:64,tolerance_ignore_poor_feature_qu:64,tolerance_unknown:64,tolerance_z:64,tolist:57,too:[6,17,20,26,29,39,40,55,59,61,62,64,92,109],toodistanterror:[92,109],took:109,tool:[2,6,24,26,33,41,42,44,64,184,196,206,207,221],tool_trajectory_in_frame2:64,toolkit:197,top:[3,6,7,14,19,20,29,37,39,44,46,49,50,55,58,60,62,64,206,210,212,215,221],top_i:57,top_land:64,topolog:[9,13,17,64],torn:64,torqu:[6,64],torque_i:109,torque_x:109,torque_z:109,total:[47,48,64,137,212],total_byt:[64,137],total_delai:212,total_mass:64,total_s:64,totimedelta:151,totla:64,touch:[2,6,8,29,64],touch_offset:29,touchdown:[2,28,29],touchdown_veloc:29,tow:62,toward:[7,38,62,64,126,189,210],towel:55,tower:14,tpp:48,traceback:[60,61,76],track:[18,26,29,36,40,53,56,62,64,80,94,95,126,177],traction:[28,29],tradit:[176,204,206,221],traffic:[43,50,202],train:[38,55,57,59,61,201,210,212],train_input_read:56,traine:61,trained_checkpoint_dir:57,trainer:61,trait:64,trajectori:[2,6,7,29,58,61,62,63,109,159,160,162,166,169,180,187],trajectory_command:[58,59,62,109],transfer:[16,23,55,56,59,127,197],transform:[6,13,16,18,40,41,61,62,64,91,94,100,107,109,189,190,197,223],transform_cloud:100,transform_cloud_from_matrix:100,transform_point:[59,100],transform_se2veloc:100,transform_se3veloc:100,transform_vec3:[62,100],transforms_snapshot:[37,58,59,64],transforms_snapshot_for_camera:[58,64],transforms_snapshot_manipulation_data:64,transient_failur:89,transientfailureerror:[62,89],transit:[2,26,28,47,64,65,80,109,188],transition_state_kneel:2,transition_state_sit:2,transition_state_sprawl:2,transition_state_stand:2,transition_state_unknown:2,translat:[2,26,29,37,61,64,74,89,100,120],translate_except:74,transmit:[40,64],transpar:64,transport:[39,53,61,62],transpos:59,trap:10,travel:[6,10,29,64,92,111,196],travel_param:[64,92,158],travelparam:92,travers:[7,10,11,16,17,18,20,64,107],treat:[19,23,53,62,64,95],tree:[13,37,40,64,91,108,155,158,223],tree_status_from_tick_statu:156,tree_to_str:158,trend:64,tri:[17,62,64,88,221],trial:28,trigger:[10,11,17,18,21,32,34,43,53,61,62,64,77,79,80,81,89,90,92,94,188,204,208],trigger_fault:94,trigger_service_fault:90,trigger_service_fault_async:90,triggerd:64,triggerservicefault:64,triggerservicefaultrespons:90,trip:[64,126,216],trivial:61,trot:[62,64],troubl:[15,92,209],troubleshoot:[44,56,57,58],trueclass:[2,64],truncat:[55,151],trunk:[60,61],trust:64,trustworthi:56,truth:[40,64],tryioctl:221,tuck:6,tunabl:40,tune:[10,59,61,164],tunnel:51,tup:158,tupl:[57,74,76,94,95,100,117,158],turn:[2,10,20,26,29,35,40,50,51,60,62,64,80,121,201,220,224],turn_param:2,tutori:[51,53,54,56,57,59,61,62,202],tweak:61,twerk:2,twerk_param:2,twice:[20,59],twilight:64,twist:64,two:[2,7,14,15,16,17,18,21,23,26,28,29,31,34,35,37,39,41,42,43,44,47,48,50,55,58,59,60,61,62,64,91,95,100,107,117,120,162,173,174,176,188,189,196,197,206,210,212,214,221],txt:[2,60,62,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,188,189,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224],tying:58,type:[1,4,7,13,14,15,19,23,24,26,28,32,34,36,37,39,40,41,43,44,45,50,51,53,57,58,60,61,62,63,73,74,75,77,78,79,80,82,86,91,92,93,94,97,98,100,101,107,108,111,112,119,125,129,130,135,138,139,140,141,142,148,149,151,153,155,158,173,196,207,221],type_bool:64,type_enum:138,type_float32:64,type_float64:64,type_float:[21,64],type_hardwar:64,type_id:[33,64,82,98],type_int16:64,type_int32:64,type_int64:64,type_int8:64,type_int:64,type_messag:64,type_nam:[64,138,142],type_softwar:64,type_str:64,type_uint16:64,type_uint32:64,type_uint64:64,type_uint8:64,type_unknown:64,type_unspecifi:64,type_url:61,typedmessagechannel:135,typic:[2,4,9,17,19,20,33,37,39,41,47,61,64,76,82,92,95,112,177,210,221],ubuntu:[27,50,51,54,55,60,62,210,212,224],udp:[43,175],ufw:[57,176],uid:64,uint16:64,uint32:[2,64],uint32valu:64,uint64:[2,64],uint64_t:64,uint8:[55,57,58],uint:[2,64],ulong:[2,64],ultim:[41,206],unabl:[40,41,64,67,77,79,86,89,92,101,221],unabletoconnecttoroboterror:[62,89],unabletoloadapptokenerror:112,unambigu:196,unansw:64,unauthenticatederror:89,unauthor:207,unavail:[89,108],unawar:[13,19,64],unbox:60,uncertain:64,uncertainti:64,uncheck:[26,62],unclear:109,uncom:59,uncompl:57,uncompress:[40,64],uncorrupt:64,undefin:39,under:[6,10,14,26,44,50,56,62,64,138],underli:[61,64,88,92,141,147],underpin:61,understand:[20,33,37,39,53,54,58,59,60,64,116,206,207],understood:64,underwai:64,undock:[62,64,86],unevenli:19,unexpect:[10,36,40,64,89,95,176,204,206,221],unexpectedli:41,unfilt:64,unfortun:50,unhealthi:64,unicod:[2,62,64],unifi:[24,64],unimpl:204,unimplementederror:89,uniniti:[64,92],uninstal:[56,60],unintend:48,unintention:60,uniqu:[2,16,19,23,24,28,32,34,37,40,41,43,44,60,64,77,79,85,93,103,112,126,136,176,196,197,201,206,207,221],unique_id:[61,64,88,108],unit:[6,16,24,40,43,51,53,59,62,64,95,121,177],unitless:64,univers:19,unix:[51,108,126,138,145,148,151],unknown:[2,19,23,36,53,55,64,77,82,89,92,100,107,108,153,155,158],unknown_edge_snapshot_id:64,unknown_waypoint_snapshot_id:64,unknowncapturetypeerror:77,unknowndnsnameerror:[62,89],unknownframeerror:[62,109],unknownimagesourceerror:93,unknownmapinformationerror:92,unknownpointcloudsourceerror:104,unknowntyp:155,unknownwaypointerror:[92,107],unkownrouteelementserror:92,unless:[14,15,17,19,26,29,31,44,61,64,107],unlik:[23,27,36,60,64,153,223],unlimit:[62,64],unload:64,unmanag:64,unmanagedresourceerror:95,unmodifi:160,unnecessari:36,unobstruct:14,unown:95,unpack:[55,57,58,64],unpopul:64,unpow:[20,45],unreach:[50,60,89,176,204,206,221],unread:196,unrecogn:[64,92],unrecongizedcommanderror:92,unrecover:[64,89],unregist:[57,62,64,75,85,89],unregisteredserviceerror:[62,108],unregisteredservicenameerror:[62,108],unregisteredservicetypeerror:[62,108],unregisterservic:[43,64],unregul:48,unrel:[41,196],unreli:[19,28,29,64],unsaf:64,unselect:26,unset:[2,6,19,39,53,62,64,76,109,126],unsetapptokenerror:112,unsetstatuserror:[89,93,104],unsign:[24,64],unspecifi:[2,33,55,64],unstitch:62,unstow:[6,62,180],unsuccess:[39,64],unsupervis:10,unsupport:[57,64],unsupportederror:109,unsupportedimageformatrequestederror:93,unsur:55,until:[10,21,24,26,33,34,41,55,57,58,59,61,62,64,75,78,80,95,105,108,109,112,125,126,129,151,173,177,189,194,201,207],unus:64,unzip:[56,60],upcom:62,updat:[13,18,23,37,41,43,44,50,64,69,70,80,85,87,92,94,95,99,103,107,108,109,112,119,126,128,130,175,178,191,204,206,207,210,212,218,219],update_frequ:[105,108,109,125],update_from:[67,69,76,77,79,81,82,83,86,87,92,98,99,105,108,109,130,153],update_from_lease_use_result:95,update_payload_vers:103,update_payload_version_async:103,update_request_iter:76,update_response_iter:76,update_user_token:108,updated_vers:[64,103],updatepayload:64,updatepayloadvers:64,updateservic:[43,64],upgrad:[44,56,60,64,209],upload:[2,11,17,18,21,25,26,28,39,44,50,57,62,64,67,92,115,183,187,196],upload_choreographed_sequ:[28,217],upload_choreographi:67,upload_choreography_async:67,upload_edge_snapshot:92,upload_graph:92,upload_graph_async:92,upload_to_aw:174,upload_to_gcp:174,upload_waypoint_snapshot:92,uploadchoreographi:[2,28],uploadedgesnapshot:[16,17,64],uploadgraph:[17,64],uploadgraphrequest:16,uploadwaypointsnapshot:[16,17,64],upon:[18,45,46,47,58,59,62,64],upper:[26,64],upper_limit:100,upper_tick_bound:153,uppermost:64,upright:[55,64,160,161,162,163,164,165,166,167,168,169,170,171,172,191,198],upsid:64,urdf:[40,47,62,64,75],url:[23,33,78],usabl:[24,62],usag:[24,26,33,35,188],usb:[16,21,26,41,50,62,64,197,206,221],use:[0,2,8,9,11,14,18,19,20,21,22,23,24,26,27,28,29,33,34,37,38,39,40,41,43,44,45,48,50,51,53,54,55,56,57,58,59,60,61,62,64,67,70,71,74,76,78,79,80,81,85,86,88,90,92,93,95,98,103,107,108,109,111,112,116,120,125,129,130,151,153,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,178,180,181,182,183,184,185,186,188,189,195,196,198,199,202,206,207,210,212,215,216,217,218,221,222,224],use_background_capture_thread:[41,94],use_display_nam:57,use_fiducial_id:[64,92],use_nano:151,used:[0,2,6,7,11,12,13,14,17,18,19,21,22,23,24,25,26,28,29,31,32,33,34,36,37,39,40,41,43,44,47,50,51,53,54,55,56,57,58,59,60,61,62,64,74,76,77,78,79,80,82,83,85,88,92,93,94,95,98,100,103,107,108,109,121,125,126,129,133,138,139,142,160,164,176,180,182,187,190,191,196,197,201,204,206,207,208,210,212,215,216,217,221,223,224],useful:[6,7,18,26,29,33,36,37,38,39,40,58,61,62,64,85,95],usehernam:60,user:[2,6,7,10,13,15,16,19,23,24,27,28,32,33,36,39,40,41,43,44,50,51,55,56,57,58,59,61,62,64,71,78,79,80,82,85,89,98,103,107,108,109,125,126,128,160,161,162,163,164,165,166,167,168,169,170,171,172,174,176,177,178,179,188,189,190,191,192,193,194,195,196,198,199,200,201,202,203,204,205,206,207,208,213,215,216,217,218,219,220,221,222,223],user_data:64,user_nam:64,user_token:[64,74,108,127],user_token_requir:[64,85],usernam:[23,26,27,39,41,43,44,50,51,55,57,58,59,60,61,64,71,108,129,160,161,162,163,164,165,166,167,168,169,170,171,172,173,175,176,177,178,179,188,189,190,191,192,193,194,195,196,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,215,216,217,218,219,220,221,222,223,224],uses:[2,7,16,17,19,20,21,23,28,32,35,37,39,40,42,43,50,53,57,58,60,61,62,64,109,126,161,164,188,189,199,200,210,224],using:[0,2,4,6,7,9,11,13,15,16,17,18,20,21,23,24,26,28,29,32,33,37,39,40,41,43,44,48,50,51,54,55,56,57,58,59,60,61,62,64,70,71,76,78,88,89,91,92,93,95,97,100,104,108,109,113,141,147,159,160,161,162,163,164,165,166,167,168,169,170,171,172,174,175,177,178,182,188,189,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,207,208,210,211,212,214,215,216,217,218,219,220,223,224],usr:[51,60,189,210],usual:[15,23,28,31,44,45,57,64,108,171,210],utf:[2,24,64],util:[18,23,38,55,57,58,59,62,63,68,70,75,88,119,131,152,174,207],util_pb2:[21,158],uuid:209,v4l2:221,v_rot:109,v_x:109,v_y:109,val:[108,126,151,173,177],valid:[15,23,28,33,35,37,39,40,41,43,50,61,62,64,71,78,80,91,95,98,107,108,109,126,127,138,148,153,155,156,207],validate_frame_tree_snapshot:91,validateframetreecycleerror:91,validateframetreedisjointerror:91,validateframetreeerror:[58,91],validateframetreeunknownframeerror:91,validationerror:[153,155],validationerrorreport:155,valu:[2,4,6,14,21,24,26,28,29,32,33,35,37,40,41,43,45,48,53,56,57,58,61,62,74,75,76,88,89,93,94,95,107,109,112,118,133,136,138,139,144,145,151,153,154,158,164,177,189,202,210,212,213,219],valuabl:10,value_from_respons:76,value_msg:158,valueerror:[76,133],vari:[2,20,28,29,33,36,64],variabl:[2,21,33,53,56,60,80,82,88,138,158,173,174,204,215],variabledeclar:[21,158],varianc:64,variant:62,variat:[2,29],varieti:[0,3,18,23,28,39,55,57,61,62,64],variou:[16,19,30,38,61,62,64,159,176,182],varnam:64,vec2:[58,59,100,109],vec2_proto:100,vec2valu:2,vec3:[37,58,59,100],vec3_proto:100,vec3trajectori:109,vec3valu:2,vector:[2,29,37,58,59,61,62,64,100,109,138,145,189],vector_alignment_with_toler:[58,64],vel:[62,100],vel_limit:[59,64,107,158],vel_of_a_in_b:91,veloc:[2,4,18,21,29,40,59,61,62,64,91,100,109,122],velocity_command:[62,109],velocity_in_frame_a:100,velocity_in_frame_b:100,velocity_in_frame_nam:64,velocity_limit:[64,92],velocity_of_a_in_c:91,velocity_of_body_in_odom:64,velocity_of_body_in_vis:[31,64],velocity_of_hand_in_odom:64,velocity_of_hand_in_vis:64,velodyn:[50,62,64,176,185],velodyne_servic:50,vendor:57,ventur:10,venv:[189,210,224],verbos:[58,59,60,61,125,129,200,215],veri:[10,16,20,28,32,39,47,50,53,61,62,64,164,189,206,207,211],verif:[39,112],verifi:[35,39,41,55,62,64,85,103,108,125,201,205,221],versa:23,versatil:42,version:[0,21,23,24,29,31,44,51,53,56,60,61,62,63,67,69,77,79,81,82,83,86,87,88,90,92,93,95,97,98,99,101,103,104,105,107,109,111,112,114,115,116,117,118,119,120,121,122,123,125,126,129,130,133,153,158,189,209,214],version_id:64,version_str:60,versioncli:124,versionservic:[62,124],versu:62,vert:58,vertex1:57,vertex2:57,vertex3:57,vertex4:57,vertex:[57,58,64],vertic:[2,4,19,26,28,29,37,64,116,162],via:[0,3,13,16,19,26,33,34,36,37,39,42,43,48,50,52,54,57,58,60,61,62,64,67,103,109,112,119,129,130,153,177,202,204,205,206,207,210,214],vice:23,video0:221,video1:221,video:[4,21,59,62,64,209,220,221],videoio:221,videoxxx:221,view:[0,2,3,4,7,11,26,28,36,38,41,42,44,51,52,54,55,57,58,62,64,116,159,181,189,197,201,205,219,221],view_map:[11,16,197],viewer:[16,26,38,57,58,59,196],viewpoint:[55,57],violat:[2,28,64],virtual:[40,41,60,64,210,212,218,224],virtualenv:[55,56,57,189,210,212,218,224],visibl:[62,64,201,206,209],vision:[6,14,37,40,58,59,62,64,91,100,109,219],vision_frame_nam:[37,58,59,62],vision_tform_bodi:[37,64],vision_tform_dogtoi:58,vision_tform_flat_bodi:59,vision_tform_hand_at_drop:[58,59],vision_tform_left_camera:37,vision_tform_obj:58,vision_tform_odom:37,vision_tform_person:59,vision_tform_robot:59,vision_tform_sensor:64,vision_tform_soccer_bal:37,vision_tform_special_fram:37,vision_tform_target:59,visit:[21,206],visual:[2,18,19,20,37,40,55,56,59,62,64,93,94,186,190,196,197,212],visual_source1:41,visual_source2_not_thread:41,visualimagesourc:[41,94],visualimagesource2:41,vlp:50,vnc:[42,50],vncconfig:51,vncpasswd:51,vncviewer64:51,volatil:64,volt:64,voltag:[4,48,64],volum:[26,115],voxel:219,vqfl7nrkvhhpouoo:196,vtk:[197,219],wai:[2,10,15,17,18,21,22,23,24,26,29,32,38,40,44,53,54,56,57,58,60,61,62,64,75,80,92,95,108,121,175,179,207],wait:[14,32,35,41,55,57,58,59,60,61,62,64,70,80,85,94,95,103,105,108,109,111,125,126,129,194,207],wait_for_stores_complet:80,wait_for_sync:[55,58,61,126],wait_heading_rt_vis:59,wait_position_rt_vis:59,wait_until_don:95,waiting_for_command:64,waitkei:58,walk:[6,7,10,14,16,21,26,38,40,45,55,57,58,59,62,64,109,160,161,165,172,180,189,190,196,197,198,207,210,224],walk_gaze_mod:64,walk_rt_vis:58,walk_to_object_in_imag:64,walk_to_object_ray_in_world:64,walkto_raycast_intersect:64,walktoobject:64,wall:[19,40,53,64,105,189],wallet:[67,76,77,79,81,82,83,86,92,95,98,105,130,153],want:[6,7,14,16,26,37,38,41,45,53,55,56,57,58,59,60,61,62,64,85,147,151,189,200,202,207,223],wantedbi:51,warm_fluoresc:64,warmstart:7,warmstart_command:64,warn:[2,6,28,33,36,40,41,57,62,64,94,177,189,206,207,215,221],wasd:[26,60,62,187,196,201,204,220],wasn:14,watch:[36,56,60,75],water:[8,45],wav:[64,115,209],wave:6,waypoint:[2,9,11,13,15,17,19,20,22,29,62,92,107,196,197,204],waypoint_dwel:29,waypoint_env:107,waypoint_environ:64,waypoint_id:[64,92],waypoint_id_list:92,waypoint_nam:[64,107],waypoint_snapshot:[16,92,196,197],waypoint_snapshot_id:[64,92],waypoint_sourc:64,waypoint_source_alternate_route_find:64,waypoint_source_robot_path:64,waypoint_source_unknown:64,waypoint_source_user_request:64,waypoint_tform_bodi:64,waypoint_tform_ko:64,waypointregion:107,waypointsnapshot:92,web:[23,34,41,43,44,50,62,103,108,174,183,186,191,206,207,215],web_cam_image_servic:221,webcam:62,webcamimageservic:34,webpag:[60,64,108,207],webrtc:[62,209],websit:[19,64],wedg:95,weigh:47,weight:[4,8,55,56,64],weightless:[176,206],well:[2,6,10,15,21,23,24,25,26,28,31,32,33,35,36,37,39,40,41,44,53,56,57,59,61,62,64,65,77,79,91,185,191,193,196,203,206,207,210,215,216,219,221],went:[57,60,84,85,90,103,105],were:[16,31,37,39,53,56,62,64,77,79,80,107,208],weren:64,what:[0,2,14,23,26,29,32,36,39,41,43,44,50,53,55,56,57,58,59,60,61,62,64,80,82,101,109,159,197,207,211],whatev:[2,29,59,61,64,98],wheel:[26,27,62],when:[2,6,8,13,14,15,16,18,19,20,21,22,23,24,26,28,29,31,32,33,36,37,39,40,41,43,44,45,47,48,53,55,57,58,59,60,61,62,64,67,70,71,76,77,79,80,82,85,88,92,93,95,98,101,103,107,108,109,112,125,126,138,151,153,160,164,179,189,191,193,194,196,201,204,206,207,208,210,212,213,215,220,221],whenev:[51,64,196],where:[0,2,3,6,7,13,16,19,20,21,24,26,27,28,29,32,33,36,37,39,40,44,47,49,52,53,55,56,57,58,59,60,61,62,64,69,78,87,92,95,99,107,108,109,130,139,173,176,177,189,196,197,202,204,205,206,207,212,221],wherea:23,wherev:[55,60],whether:[10,16,20,21,23,28,29,37,40,53,62,64,74,83,92,95,100,109,207],which:[2,6,7,10,13,14,15,16,17,18,19,20,21,23,24,25,26,27,28,29,32,33,34,35,37,39,40,41,43,44,50,51,53,55,56,57,58,59,60,61,62,64,70,75,76,77,79,80,82,85,87,92,93,94,95,98,99,101,103,104,105,107,108,109,110,111,120,126,129,130,133,137,138,139,143,145,147,148,149,151,159,165,171,173,175,176,178,179,180,183,187,188,189,191,192,193,195,196,198,199,200,201,203,204,205,206,207,208,209,210,211,213,215,216,217,219,220,221,222,223,224],whichev:29,white:[4,19,64,197,210],who:[0,39,45,50,64],whole:[18,24,26,33,61,64],whose:[64,89],why:[24,35,56,59,64],wide:[0,28,47,57,61,64],wider:[18,47,55],widest:53,width:[2,4,29,55,64],wifi:[3,4,38,39,43,44,60,62,64,75,76,204,206],wifi_password:206,wifi_radio_power_st:64,wifi_radio_power_state_off:64,wifi_radio_power_state_on:64,wifi_radio_power_state_unknown:64,wifi_ssid:206,wifi_st:64,wiki:64,wikipedia:64,wil:207,window:[19,26,27,51,54,55,58,60,62,174,189,210,212,214,218,221,224],wipe:[7,62,64],wire:[39,40,61,224],wirefram:64,wireless:[50,206],wirelessli:206,wise:64,wish:[0,20,26,29,39,64],within:[18,20,24,26,28,31,33,37,40,41,47,53,58,62,64,89,94,105,108,136,137,139,141,145,146,147,176,204,206,210,223,224],without:[6,10,14,18,19,20,24,27,28,35,39,42,43,44,45,47,50,51,60,61,62,64,71,78,80,85,108,109,112,149,175,177,196,201,207,215,223],withstand:[39,47],wolfram:64,won:[28,59,64],wonki:57,word:[53,95],work:[10,16,19,26,37,38,39,41,47,50,55,56,57,58,59,60,61,62,64,70,95,158,188,191,196,201,204,207,210,215,218,221,224],workaround:[39,62],worker:[57,62,64,101,202],workspac:[6,29,64],workspace_arm_move_param:2,world:[6,7,18,19,20,29,59,61,62,63,64,68,109,159,182,189,204,219],world_obj:130,world_obj_special_fram:37,world_object:130,world_object_apriltag:64,world_object_cli:37,world_object_dock:64,world_object_draw:64,world_object_image_coordin:64,world_object_mut:62,world_object_pb2:130,world_object_servic:68,world_object_unknown:64,world_object_with_image_coordin:223,worldobject:[37,68,130],worldobjectcli:130,worldobjectservic:[62,130],worldobjecttyp:130,worri:[55,58,126],wors:[10,64],worst:[59,64],would:[6,14,23,28,31,37,39,41,43,56,60,62,64,95,158,196],wouldn:64,wr0:[29,64],wr1:64,wrap:[2,21,34,61,64,76,80,88,109],wrapper:[64,71,88,110,126,128],wrappers_pb2:[57,58],wrench:[6,109],wrench_trajectory_in_task:64,wrenchtrajectori:6,wrestl:64,wrist:[6,64],wrist_0:2,wrist_1:2,wrist_tform_tool:[6,64],write:[6,17,21,24,26,32,33,34,36,41,55,57,58,62,64,75,78,80,81,85,88,93,103,127,134,136,138,139,145,148,180,183,215],write_data:138,write_data_block:134,write_descriptor_block:134,write_file_end:134,write_graph_and_snapshot:92,write_head:134,write_image_data:93,write_index:139,write_pgm_or_ppm:93,write_sync:[62,82],writefailederror:[108,127],writer:[64,132,139],written:[21,24,53,61,62,64,91,138,139,145,151],wrong:[6,19,39,56,57,58,59,60,61,64,71,84,85,88,90,95,103,105,108,136],wrongepocherror:95,wrote:[55,57],wrt:64,wsl:[60,61],www:60,x86_64:189,x_axi:64,xarg:209,xbb:56,xbox360:224,xbox:[62,159,187],xbox_control:[62,224],xboxdrv:224,xhat:59,xinput:224,xmax:55,xmin:55,xml:[55,56],xml_dir:56,xoffset:64,xorg:51,xrdb:51,xresourc:51,xrx:64,xry:64,xrz:64,xstartup:51,xxx:[37,151,221],xy_admitt:64,xy_to_z_cross_term_admitt:64,xyz_32f:64,xyz_4sc:64,xyz_5c:64,xyz_5sc:64,xz8c9pl5tdztqyw:196,y_axi:64,yaw:[2,15,26,29,37,43,59,61,64,100,107,109,150,224],yaw_rat:[2,29],yaw_vari:64,year:176,yellow:219,yes:[51,129],yet:[26,61,62,64,95,108,112,126,133,149],yhat:59,yield:59,yike:59,ymax:55,ymin:55,yoffset:64,you:[6,7,9,10,11,13,14,15,16,17,19,21,24,26,28,29,31,35,36,37,38,41,47,50,51,54,55,56,57,58,59,61,62,64,80,88,92,100,121,151,153,160,161,162,163,164,165,166,167,168,169,170,171,172,175,176,177,189,190,191,196,198,200,201,202,204,205,206,207,209,212,213,217,219,221,223,224],your:[2,9,12,16,18,19,22,26,36,38,50,54,55,56,57,58,59,61,62,64,80,160,161,162,163,164,165,166,167,168,169,170,171,172,174,176,189,197,198,200,201,202,204,205,206,207,210,212,214,216,220,221,223,224],your_ip:204,your_map:[16,197],your_robots_password:[55,57,58,59],youtub:28,yrx:64,yry:64,yrz:64,yyyi:176,yyyymmdd:[173,177],yyyymmdd_hhmmss:173,z_admitt:64,z_axi:64,zero:[2,21,24,29,33,56,59,64],zhat:59,zigzag:196,zip:[32,33,56,60,62],zipfil:60,zone:39,zoo:56,zoom:[21,26,64,122,197,219],zoom_limit:64,zrx:64,zry:64,zrz:64,zstd:64,zulu:176,zxvf:56},titles:["Spot SDK","Choreography Protos","spot/choreography_params.proto","Concepts","About Spot","Spot Arm and Gripper","Arm Concepts","Arm Services","Arm and gripper specifications","Autonomy","AutoReturn Service","Autonomous navigation code examples","Navigation Services","Components of Navigation","Docking","GraphNav and Robot Locomotion","GraphNav Map Structure","GraphNav Service","Autonomy Technical Summary","GraphNav Initialization","GraphNav Localization","Mission Service","Typical Autonomous Use Case","Base Services","BDDF data format","Spot Choreography SDK","Boston Dynamics Choreographer User Guide","Install Choreographer","Choreography Service","Choreography Moves Reference","Spot Data","Data Acquisition Output","Data Acquisition Overview","Data Buffer Overview","Developing API Services","E-Stop Service","Faults","Geometry and Frames","Machine Learning Bridge and External Compute","Networking","Robot Services","Integrate a Payload with the Data Acquisition Pipeline","Payload Developer Guide","Payload Software Interface","Running Custom Applications with Spot","Guidelines for Robust Payload Design","Mechanical Interfaces","Configuration Requirements","Electrical Interface","Mounting Rails","Spot CORE Cockpit - System Management Tool","Spot CORE VNC","API Protocol","Boston Dynamics API Protobuf Guidelines","Python Library","Tutorial: Playing Fetch with Spot","Fetch Part 2: Training the Model","Fetch Part 3: Evaluating the Model","Fetch Part 4: Autonomous Pick Up","Fetch Part 5: Detecting People and Playing Fetch","QuickStart","Understanding Spot Programming","Spot Release Notes","Basic Proto Definitions","arm_command.proto","Boston Dynamics Python Reference Guide","Python Choreography Client","Choreography","Python Client","Arm Surface Contact","Async Tasks","Auth","Bddf","Bddf Download","Channel","Command Line","Common","Data Acquisition","Data Acquisition Helpers","Data Acquisition Plugin","Data Acquisition Plugin Service","Data Acquisition Store","Data Buffer","Data Service","Directory","Directory Registration","Docking","Door","Estop","Exceptions","Fault","Frame Helpers","Graph Nav","Image","Image Service Helpers","Lease","License","Local Grid","Log Annotation","Manipulation Api Client","Math Helpers","Network Compute Bridge Client","Payload","Payload Registration","Point Cloud","Power","Processors","Recording","Robot","Robot Command","Robot Id","Robot State","Sdk","Server Util","SpotCAM Python Client","Audio","Compositor","Health","Lighting","Media Log","Network","Power","Ptz","Streamquality","Version","Spot Check","Time Sync","Token Cache","Token Manager","Util","World Object","Python Core","BDDF Python","Base Data Reader","Block Writer","Bosdyn","Common","Data Reader","Data Writer","File Indexer","Grpc Proto Reader","Grpc Reader","Grpc Service Reader","Grpc Service Writer","Pod Series Reader","Pod Series Writer","Protobuf Channel Reader","Protobuf Reader","Protobuf Series Writer","Stream Data Reader","Geometry","Util","Python Mission","Client","Constants","Exceptions","Remote Client","Server Util","Util","Python Examples","Arm and Mobility","Arm Door Command","Force Command","Arm Gaze Command","GCODE Drawing","Grasping","Arm JointMove Command","Arm Simple","Arm Deploy and Stow","Arm Surface Contact","Arm Trajectory","Walking to an Object","Arm and Mobility Follow","BDDF data download","Cloud Upload Example","Comms Testing","Data Acquisition Plugin Services","Using the Robot Data Service","Manipulating Spot Service Configurations in Directory","Robot Docking","Arm Examples","Autonomy and Missions Examples","Basic Service Examples","Data Acquisition Examples","Logging and Data Retrieval Examples","Payload Examples","Perception & World Objects Examples","Robot Behavior and Commands Examples","Creating an E-Stop endpoint","Follow a Fiducial","Frame Trajectory Commands","Using the Image Service","Retrieving Mission state","Using the Robot State Service","Performing Asynchronous State Queries on Spot","Using the World Object Service","GraphNav and Recording Service Command Line Interfaces","GraphNav Map Viewer","Hello Spot","Logging Through the API","Answering a Mission Question","Mission Recorder","Network Compute Bridge","Using the Payload Service","Run and Interact with a RemoteMissionService.","Replaying a Mission","Interacting with a Ricoh Theta Camera","Payload & Service Initialization","Handling Service Faults","Spot CAM Services","Spot Detect and Follow","Responding to User Interaction via Light","Spot Tensorflow Object Detection","Adjusting Robot Stance In Place","Stitch Front Spot Images Together","Tester Programs","Using the Timesync Service","Upload and Execute Choreography Sequence","Using the Velodyne Point Cloud Service","Basic Streaming Visualizer for API Messages","Controlling the Robot with a Keyboard","Image Service for a Web Cam","World Object Mutations","Using World Object Service with Image Coordinates","Controlling the Robot with an Xbox Controller"],titleterms:{"break":62,"case":[22,45],"default":64,"function":[41,61,62],"new":[41,62],AWS:174,Adding:[26,37],NOT:10,PPS:48,TLS:39,Use:22,Useful:51,Using:[10,18,39,41,54,177,191,193,195,203,204,216,218,223],about:[4,18,19],access:207,account:60,acquiredatarequest:64,acquiredatarespons:64,acquireleaserequest:64,acquireleaserespons:64,acquireplugindatarequest:64,acquireplugindatarespons:64,acquisit:[31,32,33,41,62,77,78,79,80,81,176,183,215],acquisitioncapabilitylist:64,acquisitionrequestlist:64,across:62,action:[21,32,64],actionidqueri:64,addit:[50,62,201],addlogannotationrequest:64,addlogannotationrespons:64,adjust:213,adjustparamet:64,admittanceset:64,advanc:[62,159],aggregatedentri:64,allowableorient:64,angularinterpol:64,annot:[24,64,98],annotationsentri:64,annotationst:64,announc:207,answer:200,answeredquest:64,answerquestionrequest:64,answerquestionrespons:64,api:[3,28,34,43,52,53,62,99,199,219],apigraspoverrid:64,apigraspoverriderequest:64,apigraspoverriderespons:64,applic:44,apriltag:189,apriltagposestatu:64,apriltagproperti:64,architectur:32,area:64,arm:[5,6,7,8,29,47,62,69,160,161,163,166,167,168,169,170,172,180],arm_command:64,arm_mov:29,arm_move_rel:29,arm_surface_contact:64,arm_surface_contact_servic:64,armcartesiancommand:64,armcommand:[6,64],armdragcommand:64,armjointmovecommand:64,armjointposit:64,armjointtrajectori:64,armjointtrajectorypoint:64,armjointveloc:64,armmovefram:2,armmoveparam:2,armparam:64,armstopcommand:64,armsurfacecontact:[7,64],armsurfacecontactcommand:64,armsurfacecontactrespons:64,armsurfacecontactservic:64,armvelocitycommand:64,associatedmetadata:64,async:70,asynchron:[61,194],attach:41,audio:[64,115],audiocapturechannel:64,audioservic:64,auth:[23,64,71],auth_servic:64,authent:61,author:[33,43,61,62],authservic:64,autograspcommand:64,autonom:[11,15,22,58,62],autonomi:[9,18,181],autoreturn:10,autowalk:[174,204],avoid:6,awbmod:64,awbmodeenum:64,axismod:64,background:41,base:[23,133],basic:[26,34,63,173,182,219],basic_command:64,batterychangeposecommand:64,batteryst:64,bddf:[24,33,64,72,73,132,173],behavior:[21,36,62,187],behaviorfault:64,behaviorfaultst:64,beta:62,between:[37,207],big:8,blackboard:21,blob:33,blobpag:64,blobspec:64,block:[26,61,134],blockentri:64,bodi:[6,29],body_const:29,body_hold:29,bodycontrolparam:64,bodyexternalforceparam:64,bodyholdparam:2,bodymovementstatu:64,boot:51,bosdyn:[62,135],bosdyndockst:64,bosdyngraphnavloc:64,bosdyngraphnavst:64,bosdynnavigateto:64,bosdynpowerrequest:64,bosdynrobotcommand:64,bosdynrobotst:64,boston:[26,33,53,65],bound:[43,64],bourre:29,bourreeparam:2,box2:64,box2withfram:64,box3:64,box3withfram:64,box:43,bridg:[38,62,101,202],brightnessesentri:64,buffer:[33,39,62,82],bug:62,build:[44,200],butt_circl:29,buttcircleparam:2,cabl:[45,46],cach:127,calibr:64,callback:204,cam:[62,209,221],camera:[4,41,61,64,197,206,219],cameracalibrationcommandrequest:64,cameracalibrationcommandrespons:64,cameracalibrationfeedbackrequest:64,cameracalibrationfeedbackrespons:64,camerainterfac:41,cameraintrins:64,cameraresultsentri:64,can:[8,60,62],cancelacquisitionrequest:64,cancelacquisitionrespons:64,capabl:[32,41],captur:[32,41,61],captureactionid:64,captureparamet:64,cartesian:6,cartesianveloc:64,categori:[2,48],caus:64,cellformat:64,center:43,chalk:164,chang:[62,213],channel:[32,74,146],check:[50,125],checkin:35,checksumtyp:64,chicken_head:29,chickenheadparam:2,childtoparentedgemapentri:64,choic:39,choreograph:[26,27,62],choreographerdisplayinfo:2,choreographersav:2,choreographi:[1,25,26,28,29,62,66,67,217],choreography_param:2,choreography_sequ:2,choreography_servic:2,choreographysequ:2,choreographyservic:2,circl:64,circle2d:64,clapparam:2,clawgrippercommand:64,clear:61,clearanc:47,clearbehaviorfaultrequest:64,clearbehaviorfaultrespons:64,clearbiteventsrequest:64,clearbiteventsrespons:64,cleargraphrequest:64,cleargraphrespons:64,clearservicefaultrequest:64,clearservicefaultrespons:64,client:[38,62,66,68,99,101,114,152,153,156,202,207],cloud:[62,104,174,218],cockpit:50,code:[11,62,64,65],collect:41,collis:6,color:[2,64],colormap:64,comm:175,command:[40,44,60,61,62,64,75,109,161,162,163,166,187,188,190,196,201],comment:[33,173,206],common:[76,136,160,161,162,163,164,165,166,167,168,169,170,171,172,198],commonerror:64,commsstat:64,commun:[48,62,176,207],compar:64,completionbehavior:64,complex:21,compon:[13,38],compositor:[64,116],compositorservic:64,compress:64,comput:[38,44,62,101,202],concept:[3,6,18,32,38],condit:[28,64],config:[14,28],configrang:64,configur:[10,35,43,44,47,61,178,206],connect:[4,26,206],connector:46,consist:62,constant:154,constantresult:64,constantvalu:64,constraint:20,contact:[64,69,169],contain:[44,50],content:[0,1,3,5,9,25,30,42,52,54,55,63,65,66,68,114,131,132,152,159,180,181,182,183,184,185,186,187],control:[16,26,62,197,219,220,224],coordin:223,copi:[26,60],core:[50,51,131,207],correct:46,crash:45,crawl:29,crawlparam:2,creat:[16,21,41,44,51,61,188],createedgerequest:64,createedgerespons:64,createwaypointrequest:64,createwaypointrespons:64,credenti:174,csv:31,custom:44,cyclepowerrequest:64,cyclepowerrespons:64,cylindricalcoordin:64,cylindricalveloc:64,data:[18,24,30,31,32,33,38,41,62,77,78,79,80,81,82,83,133,137,138,149,173,176,177,183,184,215],data_acquisit:64,data_acquisition_plugin_servic:64,data_acquisition_servic:64,data_acquisition_stor:64,data_acquisition_store_servic:64,data_buff:64,data_buffer_servic:64,data_chunk:64,data_index:64,data_servic:64,dataacquisit:64,dataacquisitioncap:64,dataacquisitionpluginservic:64,dataacquisitionservic:64,dataacquisitionstoreservic:64,datablob:64,databufferservic:64,databufferstatu:64,datacaptur:64,datachunk:64,datadescriptor:64,dataerror:64,dataidentifi:64,dataindex:64,dataqueri:64,dataqueryparam:64,dataservic:64,datetoblackboard:64,db25:46,debug:[206,221],debugrequest:64,debugrespons:64,defin:61,defineblackboard:64,definit:[38,63],degrad:64,degradationtyp:64,delet:26,delete_pag:177,deletedatapagesrequest:64,deletedatapagesrespons:64,deletepagestatu:64,deleterequest:64,deleterespons:64,deletesoundrequest:64,deletesoundrespons:64,demo:207,depend:[62,65,160,161,162,163,164,165,166,167,168,169,170,171,172,173,175,176,177,178,179,188,189,191,192,193,194,195,196,197,198,199,200,201,203,204,205,208,209,211,213,215,216,217,218,219,220,222,223,224],deploi:168,deprec:62,depthplanespotcheckresult:64,deregisterestopendpointrequest:64,deregisterestopendpointrespons:64,descriptorblock:64,desert:20,design:45,detail:164,detect:[59,210,212],develop:[34,42,62,206],devic:43,diagram:202,dimens:[4,46,49],directionconstraint:64,directionhint:64,directori:[23,34,41,64,84,85,178],directory_registr:64,directory_registration_servic:64,directory_servic:64,directoryregistrationservic:[43,64],directoryservic:[43,64],discov:38,discoveri:39,distanc:189,distribut:60,dock:[14,62,64,86,179],dockedstatu:64,docker:[44,50,175,176,206,210,221],docking_servic:64,dockingcommandfeedbackrequest:64,dockingcommandfeedbackrespons:64,dockingcommandrequest:64,dockingcommandrespons:64,dockingservic:64,dockproperti:64,dockstat:64,docktyp:64,document:[50,62],door:[7,64,87,161],door_servic:64,doorcommand:64,doorservic:64,download:[16,32,33,73,173,176],downloadedgesnapshotrequest:64,downloadedgesnapshotrespons:64,downloadgraphrequest:64,downloadgraphrespons:64,downloadwaypointsnapshotrequest:64,downloadwaypointsnapshotrespons:64,drag:6,draw:164,drawablearrow:64,drawablebox:64,drawablecapsul:64,drawablecylind:64,drawablefram:64,drawablelinestrip:64,drawablepoint:64,drawableproperti:64,drawablespher:64,dure:37,dynam:[26,29,33,53,65],eas:2,edg:[18,20,45,64],edgesnapshot:64,edgesourc:64,electr:48,empti:64,enabl:[45,51],enablecongestioncontrolrequest:64,enablecongestioncontrolrespons:64,encod:64,encodingparamet:64,endpoint:[61,64,188,189,211],entri:28,environ:[4,18,60],environment:62,error:[15,39,41,64],errorcod:64,establish:61,establishsessionrequest:64,establishsessionrespons:64,estim:62,estop:[40,64,88],estop_servic:64,estopcheckinrequest:64,estopcheckinrespons:64,estopconfig:64,estopendpoint:64,estopendpointwithstatu:64,estopservic:64,estopst:64,estopstoplevel:64,estopsystemstatu:64,eulerratezyxvalu:2,eulerzyxvalu:2,evalu:57,event:[33,64,173],eventscom:64,eventscommentsspec:64,eventspec:64,exampl:[11,16,21,33,34,38,41,43,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,203,204,205,208,209,211,213,216,217,218,219,220,221,222,223],except:[62,89,155],execut:[196,202,210,212,214,217,221,224],executechoreographyrequest:2,executechoreographyrespons:2,exit:28,expand:62,expos:62,extent:43,extern:[38,45,189],externalforceind:64,failednod:64,fault:[36,43,62,90,208],fault_servic:64,faultservic:64,featur:[20,62],featureenabledentri:64,featurequalitytoler:64,feedback:[6,15,62,64],fetch:[55,56,57,58,59],fiduci:[19,189,201],fiducialinit:64,figure8_mov:29,figure8param:2,file:[24,26,28,31,139],fileformatdescriptor:64,fileformatvers:64,fileindex:64,finish:10,fix:62,follow:[172,189,210],followarmcommand:[6,64],footheightcheckresult:64,footheightresultsentri:64,footpositionsentri:64,footstat:64,forc:[6,162],fordur:64,format:[24,33,64],formatblackboard:64,forward:43,frame:[6,37,61,62,91,190],frametreesnapshot:64,freezecommand:64,from:[16,60,176],front:[46,214],front_up:29,frontupparam:2,full:60,full_body_command:64,fullbodycommand:64,fundament:61,gaze:[6,163],gazecommand:64,gcode:164,gcp:174,geometri:[19,37,46,64,150],get:60,get_com:177,get_ev:177,get_index:177,get_pag:177,getaudiocapturechannelrequest:64,getaudiocapturechannelrespons:64,getaudiocapturegainrequest:64,getaudiocapturegainrespons:64,getauthtokenrequest:64,getauthtokenrespons:64,getbitstatusrequest:64,getbitstatusrespons:64,getdatabufferstatusrequest:64,getdatabufferstatusrespons:64,getdataindexrequest:64,getdataindexrespons:64,getdatapagesrequest:64,getdatapagesrespons:64,getdockingconfigrequest:64,getdockingconfigrespons:64,getdockingstaterequest:64,getdockingstaterespons:64,getestopconfigrequest:64,getestopconfigrespons:64,getestopsystemstatusrequest:64,getestopsystemstatusrespons:64,geteventscommentsrequest:64,geteventscommentsrespons:64,getfeatureenabledrequest:64,getfeatureenabledrespons:64,geticeconfigurationrequest:64,geticeconfigurationrespons:64,getimagerequest:64,getimagerespons:64,getinforequest:64,getinforespons:64,getircolormaprequest:64,getircolormaprespons:64,getledbrightnessrequest:64,getledbrightnessrespons:64,getlicenseinforequest:64,getlicenseinforespons:64,getlocalgridsrequest:64,getlocalgridsrespons:64,getlocalgridtypesrequest:64,getlocalgridtypesrespons:64,getlocalizationstaterequest:64,getlocalizationstaterespons:64,getmissionrequest:64,getmissionrespons:64,getnetworksettingsrequest:64,getnetworksettingsrespons:64,getpayloadauthtokenrequest:64,getpayloadauthtokenrespons:64,getpointcloudrequest:64,getpointcloudrespons:64,getpowerstatusrequest:64,getpowerstatusrespons:64,getptzpositionrequest:64,getptzpositionrespons:64,getptzvelocityrequest:64,getptzvelocityrespons:64,getrecordstatusrequest:64,getrecordstatusrespons:64,getscreenrequest:64,getscreenrespons:64,getserviceentryrequest:64,getserviceentryrespons:64,getserviceinforequest:64,getserviceinforespons:64,getsoftwareversionrequest:64,getsoftwareversionrespons:64,getsslcertrequest:64,getsslcertrespons:64,getstaterequest:64,getstaterespons:64,getstatusrequest:64,getstatusrespons:64,getstreamparamsrequest:64,getstreamparamsrespons:64,gettemperaturerequest:64,gettemperaturerespons:64,getvisiblecamerasrequest:64,getvisiblecamerasrespons:64,getvolumerequest:64,getvolumerespons:64,github:60,gland:46,graph:[62,64,92],graph_nav:64,graph_nav_servic:64,graphnav:[15,16,17,18,19,20,196,197],graphnavrecordingservic:[17,64],graphnavservic:[17,64],grasp:[8,165],graspparam:64,grasppositionconstraint:64,green:26,grid:[40,97],gripper:[5,8,29,62],gripper_command:64,grippercommand:64,gripperparam:2,group:32,grpc:[34,39,140,141,142,143,173],grpcpage:64,grpcspec:64,gui:188,guid:[26,42,65,210,212,224],guidelin:[36,45,53],hand:6,handl:[39,62,208],handlestal:64,handletyp:64,happen:10,hardwareconfigur:64,header:64,health:[64,117],healthservic:64,height:47,hello:[60,198],helper:[62,78,91,94,100],high:32,hingesid:64,hip:4,hiprangeofmotionresult:64,hiprangeofmotionresultsentri:64,hop:29,hopparam:2,how:[8,10,61,201],http:39,iceserv:64,identifi:24,imag:[40,41,44,61,64,93,94,191,206,214,215,221,223],image_servic:64,imageacquisitioncap:64,imagecaptur:64,imageparam:64,imageproperti:64,imagerequest:64,imagerespons:64,imageservic:64,imagesourc:64,imagesourceandservic:64,imagesourcecaptur:64,imagetyp:64,implement:32,improv:62,independ:60,index:139,inertia:43,infrastructur:34,ingress:45,initi:[18,19,207],initializelensrequest:64,initializelensrespons:64,inspect:61,instal:[27,51,60,174,202,206,210,212,214,224],instruct:224,integr:[41,62],interact:[204,206,211],interfac:[26,34,43,46,48,196],interfer:[45,47],intermedi:159,intern:33,interv:20,introduct:42,introductori:159,ircolormap:64,irmeteroverlai:64,isometr:47,issu:[62,214],item:[206,214],jog:29,joint:[4,6,8],jointkinematiccheckresult:64,jointlimit:64,jointmov:166,jointstat:64,joystick:26,json:31,jump:29,jumpparam:2,keepout:46,kei:18,keyboard:[26,201,220],keytoseriesidentifierhashentri:64,keyvalu:64,kinematiccalresultsentri:64,kinematicst:64,kneel:29,kneel_circl:29,kneel_leg_mov:29,kneel_leg_move2:29,kneel_to_stand:29,kneel_to_stand_fast:29,kneelcircleparam:2,kneellegmove2param:2,kneellegmoveparam:2,known:[62,214],land:64,layer:28,lead:2,learn:38,leas:[61,62,64,95],lease_servic:64,leaseown:64,leaseresourc:64,leaseservic:64,leaseuseresult:64,led:64,leg:[2,45,47],legpaircheckresult:64,legpairresultsentri:64,length:[8,47],let:60,level:[32,64],librari:[54,189],licens:[62,64,96],license_servic:64,licenseinfo:64,licenseservic:64,light:[118,211],lightingservic:64,limit:[6,64],line:[44,62,75,188,196],link:[8,62,64],linkstatu:64,list:[41,61],listallmovesrequest:2,listallmovesrespons:2,listavailablemodelsrequest:64,listavailablemodelsrespons:64,listavailablemodelsstatu:64,listcamerasrequest:64,listcamerasrespons:64,listcaptureactionsrequest:64,listcaptureactionsrespons:64,listimagesourcesrequest:64,listimagesourcesrespons:64,listleasesrequest:64,listleasesrespons:64,listlogpointsrequest:64,listlogpointsrespons:64,listpayloadsrequest:64,listpayloadsrespons:64,listpointcloudsourcesrequest:64,listpointcloudsourcesrespons:64,listptzrequest:64,listptzrespons:64,listscreensrequest:64,listscreensrespons:64,listserviceentriesrequest:64,listserviceentriesrespons:64,listsoundsrequest:64,listsoundsrespons:64,liststoreddatarequest:64,liststoreddatarespons:64,liststoredimagesrequest:64,liststoredimagesrespons:64,liststoredmetadatarequest:64,liststoredmetadatarespons:64,listworldobjectrequest:64,listworldobjectrespons:64,load:[26,45],loadcellresultsentri:64,loadcellspotcheckresult:64,loadmissionrequest:64,loadmissionrespons:64,loadsoundrequest:64,loadsoundrespons:64,local:[18,19,20,40,44,64,97,201],local_grid:64,local_grid_servic:64,localgrid:64,localgridext:64,localgridrequest:64,localgridrespons:64,localgridservic:64,localgridtyp:64,localizeregion:64,locomot:[15,62],locomotionhint:64,log:[33,62,64,98,119,184,199],log_annot:64,log_annotation_servic:64,logannot:64,logannotationlogblob:64,logannotationoperatormessag:64,logannotationservic:64,logannotationtextmessag:64,logpoint:64,logstatu:64,lost:[18,20],lostdetectorst:64,machin:38,manag:[44,50,60,128],manipul:[7,99,178],manipulation_api:64,manipulation_api_servic:64,manipulationapifeedbackrequest:64,manipulationapifeedbackrespons:64,manipulationapirequest:64,manipulationapirespons:64,manipulationapiservic:64,manipulationcamerasourc:64,manipulationfeedbackst:64,manipulatorst:64,map:[16,18,62,64,197],mapstat:64,mass:43,math:[37,100],matrix:64,mechan:46,media:119,medialogservic:64,messag:[33,61,219],messagetypedescriptor:64,metadata:[31,41,64],miscellan:62,mission:[21,62,64,152,181,192,200,201,204,205],mission_question_answer:200,mission_servic:64,missioninfo:64,missionservic:[21,64],mobil:[6,62,160,172],mobility_command:64,mobilitycommand:64,mobilityparam:64,mode:[26,64],model:[38,56,57],modifi:26,moment:43,momentofintertia:64,more:21,motion:8,motiv:24,motor:[48,61],motorpowerst:64,mount:49,move:[6,26,29,60,61,62],moveinfo:2,moveinfoconfig:28,movement:189,moveparam:2,moveparamsconfig:28,multipl:[26,60],music:26,mutat:[64,222],mutateworldobjectrequest:64,mutateworldobjectrespons:64,namedarmpositionscommand:64,nav:[62,64,92],navig:[11,12,13,15,17,62],navigaterout:15,navigaterouterequest:64,navigaterouterespons:64,navigateto:15,navigatetorequest:64,navigatetorespons:64,navigationfeedbackrequest:64,navigationfeedbackrespons:64,network:[39,43,50,62,64,101,120,202],network_compute_bridg:64,network_compute_bridge_servic:64,networkcomputebridg:[38,64],networkcomputebridgework:[38,64],networkcomputeinputdata:64,networkcomputerequest:64,networkcomputerespons:64,networkcomputeserverconfigur:64,networkcomputestatu:64,networkservic:64,networktupl:64,next:60,nod:29,node:[21,64,200],nodeinfo:64,nodest:64,nodestatesattick:64,non:41,normalizedcoordin:64,note:[6,18,28,62],object:[8,40,61,130,171,186,195,212,219,222,223],objmodel:64,obstacleparam:64,off:61,offset:213,one:204,onli:210,opendoorcommandrequest:64,opendoorcommandrespons:64,opendoorfeedbackrequest:64,opendoorfeedbackrespons:64,oper:[33,35,37,173],operand:64,operatorcom:64,option:[62,64],organ:24,orient:[43,46],origin:20,other:[20,38,41,62],output:31,over:62,overrid:64,overview:[26,28,32,33,51,201],ownership:61,pace2stepparam:2,pace:29,pace_2step:29,packag:[60,174,206,214],pageformat:64,pageinfo:64,pagesandtimestamp:64,paramet:[26,64],parentedg:64,part:[56,57,58,59,204],password:51,past:26,pausemissionrequest:64,pausemissionrespons:64,payload:[4,41,42,43,44,45,47,48,62,64,102,103,185,203,207],payload_estim:64,payload_registr:64,payload_registration_servic:64,payload_servic:64,payloadcheckresult:64,payloadestimationcommand:64,payloadmassvolumeproperti:64,payloadportspowerst:64,payloadpreset:64,payloadregistrationservic:[43,64],payloadservic:[43,64],peopl:59,per:48,percept:186,perform:[26,62,194],pick:58,pickobject:64,pickobjectexecuteplan:64,pickobjectinimag:64,pickobjectrayinworld:64,pin:48,pinch:8,ping:60,pinholeintrins:64,pinholemodel:64,pinout:48,pip:60,pipelin:41,pivot:2,pixelformat:64,place:[19,213],plai:[55,59],plane:64,platform:54,playmissionrequest:64,playmissionrespons:64,playset:64,playsoundrequest:64,playsoundrespons:64,plugin:[41,62,79,80,176,215],pluginserviceerror:64,pod:[144,145],podtypedescriptor:64,podtypeenum:64,point:[8,49,62,104,218],point_cloud:64,point_cloud_servic:64,pointcloud:64,pointcloudrequest:64,pointcloudrespons:64,pointcloudservic:64,pointcloudsourc:64,polygon:64,polygonwithexclus:64,polylin:64,poor:62,port:[43,47,48],portain:44,pose:6,posit:[43,64],positionalinterpol:64,post:38,power:[4,40,48,61,62,64,105,121],power_servic:64,powercommandfeedbackrequest:64,powercommandfeedbackrespons:64,powercommandrequest:64,powercommandrespons:64,powercommandstatu:64,powerservic:64,powerst:64,powerstatu:64,pre:38,predefin:6,prepar:[51,62],prepposebehavior:64,prerequisit:[50,221],preview:26,problem:[160,161,162,163,164,165,166,167,168,169,170,171,172,198],process:38,processor:106,program:[61,160,161,162,163,164,165,166,167,168,169,170,171,172,198,215],prompt:[64,200],properti:43,protect:45,proto:[1,2,63,64,140],protobuf:[53,61,62,146,147,148],protocol:[39,52],ptz:[64,122],ptzdescript:64,ptzposit:64,ptzservic:64,ptzveloc:64,puls:48,python:[16,54,60,61,65,66,68,114,131,132,152,159],quaternion:64,queri:[60,194],question:[64,200],queuestatu:64,quickstart:60,radian:43,rail:49,random_rot:29,random_stretch:29,randomrotateparam:2,rang:[8,177],raw:33,reader:[133,137,140,141,142,144,146,147,149],recommend:159,record:[62,64,107,196,201,204],recorddatablobsrequest:64,recorddatablobsrespons:64,recordeventsrequest:64,recordeventsrespons:64,recording_servic:64,recordingenviron:64,recordoperatorcommentsrequest:64,recordoperatorcommentsrespons:64,recordsignalticksrequest:64,recordsignalticksrespons:64,recordtextmessagesrequest:64,recordtextmessagesrespons:64,recordtyp:64,red:26,refer:[29,49,65],regist:[34,43,61,207],registerestopendpointrequest:64,registerestopendpointrespons:64,registerpayloadrequest:64,registerpayloadrespons:64,registerservicerequest:64,registerservicerespons:64,registersignalschemarequest:64,registersignalschemarespons:64,registr:[35,41,43,85,103,207],rel:46,releas:62,reliabl:28,remot:[64,156,204],remote_servic:64,remotegrpc:64,remotemissionservic:[21,64,204],remotepointcloudstatu:64,remov:62,renam:62,repeat:64,replai:[200,201,205],report:41,request:[6,32,60,64,173],requesthead:64,requir:[27,47,48,60,206,214],resourc:51,respond:211,respons:173,responsehead:64,restartmissionrequest:64,restartmissionrespons:64,restrict:26,result:64,retainleaserequest:64,retainleaserespons:64,retri:64,retriev:[61,184,192],retrieverawdatarequest:64,retrieverawdatarespons:64,retrieverequest:64,retrieverespons:64,returnleaserequest:64,returnleaserespons:64,ricoh:206,right:45,robot:[4,14,15,18,20,23,26,34,37,39,40,46,47,48,60,61,62,108,109,110,111,177,179,187,189,193,204,213,220,224],robot_command:64,robot_command_servic:64,robot_id:64,robot_id_servic:64,robot_st:64,robot_state_servic:64,robotcommand:64,robotcommandfeedback:64,robotcommandfeedbackrequest:64,robotcommandfeedbackrespons:64,robotcommandfeedbackstatu:64,robotcommandrequest:64,robotcommandrespons:64,robotcommandservic:64,robothardwareconfigurationrequest:64,robothardwareconfigurationrespons:64,robotid:64,robotidrequest:64,robotidrespons:64,robotidservic:64,robotlinkmodelrequest:64,robotlinkmodelrespons:64,robotmetr:64,robotmetricsrequest:64,robotmetricsrespons:64,robotpowerst:64,robotsoftwarereleas:64,robotst:64,robotstaterequest:64,robotstaterespons:64,robotstateservic:64,robust:45,rom:47,rotate_bodi:29,rotate_body_sharp:29,rotatebodyparam:2,rotateimag:64,rotationset:64,rotationwithtoler:64,rout:64,routegenparam:64,rpc:[17,21,38,43,68,152],run:[26,27,41,44,60,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,188,189,190,191,192,193,194,195,197,198,199,203,204,205,206,208,209,210,211,213,216,217,218,219,220,221,222,223],running_man:29,runningmanparam:2,safe:10,safepoweroffcommand:64,safeti:[6,8,26,48],sampl:62,save:26,scalartrajectori:64,scalartrajectorypoint:64,scalingpair:64,screendescript:64,script:207,sdk:[0,25,60,61,112],se2pos:64,se2trajectori:64,se2trajectorycommand:[62,64],se2trajectorypoint:64,se2veloc:64,se2velocitycommand:64,se2velocitylimit:64,se3covari:64,se3pos:64,se3trajectori:64,se3trajectorypoint:64,se3veloc:64,seal:46,search:19,second:48,see:60,select:[26,207],selector:64,self:[43,45],self_right:29,selfrightcommand:64,sens:4,sequenc:[21,26,64,217],seri:[24,144,145,148],seriesblockindex:64,seriesdescriptor:64,seriesidentifi:64,server:[34,38,51,113,157,202,204],servertyp:64,servic:[7,10,12,14,17,21,23,28,32,33,34,35,36,40,41,43,50,61,62,64,80,83,94,142,143,176,177,178,182,191,193,195,196,203,206,207,208,209,215,216,218,221,223],service_fault:64,serviceentri:64,servicefault:64,servicefaultid:64,servicefaultst:64,set:[50,207],setaudiocapturechannelrequest:64,setaudiocapturechannelrespons:64,setaudiocapturegainrequest:64,setaudiocapturegainrespons:64,setblackboard:64,setestopconfigrequest:64,setestopconfigrespons:64,seticeconfigurationrequest:64,seticeconfigurationrespons:64,setircolormaprequest:64,setircolormaprespons:64,setirmeteroverlayrequest:64,setirmeteroverlayrespons:64,setledbrightnessrequest:64,setledbrightnessrespons:64,setlocalizationrequest:64,setlocalizationrespons:64,setpassphraserequest:64,setpassphraserespons:64,setpowerstatusrequest:64,setpowerstatusrespons:64,setptzpositionrequest:64,setptzpositionrespons:64,setptzvelocityrequest:64,setptzvelocityrespons:64,setrecordingenvironmentrequest:64,setrecordingenvironmentrespons:64,setscreenrequest:64,setscreenrespons:64,setstreamparamsrequest:64,setstreamparamsrespons:64,setup:[50,60,61,160,161,162,163,164,165,166,167,168,169,170,171,172,173,175,176,177,178,179,188,189,191,192,193,194,195,196,197,198,199,200,201,203,204,205,208,209,211,213,215,216,217,218,219,220,222,223],setvolumerequest:64,setvolumerespons:64,sever:64,shock:45,shorepowerst:64,shoulder_left:29,shoulder_right:29,side:2,sideparam:2,signal:33,signalschema:64,signalschemaid:64,signaltick:64,simpl:[21,167],simpleparallel:64,sit:29,sit_to_sprawl:29,sitcommand:64,skeleton:64,skip:29,sleep:64,slice:28,slider:26,softwar:[43,50,51,61],softwarevers:64,sound:64,span:173,specentri:64,specif:[4,8,48,224],specifi:[62,173],sphericallimit:64,spot:[0,2,3,4,5,14,25,30,37,42,44,49,50,51,55,60,61,62,64,125,160,161,162,163,164,165,166,167,168,169,170,171,172,178,194,198,206,207,209,210,212,214],spot_cam:64,spot_check:64,spot_check_servic:64,spotcam:[62,114],spotcamptz:64,spotcamstoremedia:64,spotcheckcommandrequest:64,spotcheckcommandrespons:64,spotcheckfeedbackrequest:64,spotcheckfeedbackrespons:64,spotcheckservic:64,squeezegrasp:64,stair:[20,64],stairdata:64,stanc:[64,213],stancecommand:64,stand_to_kneel:29,stand_up:29,standard:[2,64,65],standcommand:64,start:[34,51,200,204,207],startrecordingrequest:64,startrecordingrespons:64,state:[14,40,61,62,64,111,192,193,194],statu:[2,15,64],step:[29,60,204,206,214],stepparam:2,stitch:214,stop:[6,35,60,61,188,189,190,211,217],stopcommand:64,stoprecordingrequest:64,stoprecordingrespons:64,stoprequest:64,stoprespons:64,store:[81,176],storedatarequest:64,storedatarespons:64,storeimagerequest:64,storeimagerespons:64,storemetadatarequest:64,storemetadatarespons:64,storerequest:64,storerespons:64,stow:[29,168],stowstat:64,straightstaircas:64,stream:[64,149,219],streamparam:64,streamqual:[64,123],streamqualityservic:64,structtypedescriptor:64,structur:[16,21,24,31,34,197],stuck:20,summari:18,support:[33,54,62],surfac:[69,169],suspectedambigu:64,swai:29,swayparam:2,swaystyl:2,swingdirect:64,swingheight:64,sync:[23,126],synchronized_command:64,synchronizedcommand:[6,64],system:[27,32,36,38,50,60,202],systemfault:64,systemfaultst:64,tabl:43,tablet:16,tagrequest:64,tagrespons:64,take:[60,61],takeleaserequest:64,takeleaserespons:64,task:70,tcp:39,teardownsessionrequest:64,teardownsessionrespons:64,technic:18,temperatur:64,tensor:43,tensorflow:212,termin:50,terminolog:28,terrainparam:64,test:[41,44,175,176,206,215],tester:215,text:33,textmessag:64,textur:19,theta:206,thread:41,through:199,tickrequest:64,tickrespons:64,tigervnc:51,time:[23,126,173,177],time_rang:64,time_sync:64,time_sync_servic:64,timerang:64,timerangequeri:64,timesync:[61,216],timesyncestim:64,timesyncroundtrip:64,timesyncservic:64,timesyncst:64,timesyncupdaterequest:64,timesyncupdaterespons:64,tip:[41,206,221],togeth:214,token:[127,128],tool:[50,62],top:47,total:43,track:28,train:56,trajectori:[64,170,190],transform:37,transit:29,transitionst:2,travelparam:64,tree:21,triggerservicefaultrequest:64,triggerservicefaultrespons:64,trot:29,troubleshoot:202,turn_2step:29,turnparam:2,tutori:55,twerk:29,twerkparam:2,two:204,txt:28,type:[2,21,33,64],typic:[14,22],under:47,understand:[61,160,161,162,163,164,165,166,167,168,169,170,171,172,197,198],undock:14,unregisterservicerequest:64,unregisterservicerespons:64,unstow:29,updat:[20,62],updatepayloadversionrequest:64,updatepayloadversionrespons:64,updateservicerequest:64,updateservicerespons:64,upload:[16,174,217],uploadchoreographyrequest:2,uploadchoreographyrespons:2,uploadedgesnapshotrequest:64,uploadedgesnapshotrespons:64,uploadgraphrequest:64,uploadgraphrespons:64,uploadwaypointsnapshotrequest:64,uploadwaypointsnapshotrespons:64,usag:[14,62],use:10,user:[26,60,210,211,212,224],userdata:64,using:[176,206,221],util:[64,113,129,151,157,158],valu:[64,207],variabl:64,variabledeclar:64,vec2:64,vec2valu:64,vec3:64,vec3trajectori:64,vec3trajectorypoint:64,vec3valu:64,vectoralignmentwithtoler:64,veloc:6,velodyn:218,verifi:60,version:[50,54,64,124,188],versionservic:64,via:211,view:[16,47,61],viewer:[51,197],virtualenv:60,visual:219,vnc:51,vncserver:51,vncviewer:51,volum:64,walk:171,walkgazemod:64,walktoobjectinimag:64,walktoobjectrayinworld:64,want:[10,210],warmstartcommand:64,waypoint:[16,18,64],waypointsnapshot:64,waypointsourc:64,web:221,weight:47,what:[10,28],when:10,why:10,width:47,wifi:50,wifiradiopowerst:64,wifist:64,window:64,without:[188,204,210],workspace_arm_mov:29,workspacearmmoveparam:2,world:[37,40,130,186,195,222,223],world_object:64,world_object_servic:64,worldobject:64,worldobjectservic:64,worldobjecttyp:64,would:10,wrench:64,wrenchtrajectori:64,wrenchtrajectorypoint:64,writer:[134,138,143,145,148],xbox:224,xyz:43,you:[60,210],your:60,zip:31,zxy:43}}) \ No newline at end of file +Search.setIndex({docnames:["README","choreography_protos/bosdyn/api/README","choreography_protos/bosdyn/api/choreography_reference","docs/concepts/README","docs/concepts/about_spot","docs/concepts/arm/README","docs/concepts/arm/arm_concepts","docs/concepts/arm/arm_services","docs/concepts/arm/arm_specification","docs/concepts/autonomy/README","docs/concepts/autonomy/auto_return","docs/concepts/autonomy/autonomous_navigation_code_examples","docs/concepts/autonomy/autonomous_navigation_services","docs/concepts/autonomy/components_of_autonomous_navigation","docs/concepts/autonomy/directed_exploration","docs/concepts/autonomy/docking","docs/concepts/autonomy/graphnav_and_robot_locomotion","docs/concepts/autonomy/graphnav_map_structure","docs/concepts/autonomy/graphnav_service","docs/concepts/autonomy/graphnav_tech_summary","docs/concepts/autonomy/initialization","docs/concepts/autonomy/localization","docs/concepts/autonomy/missions_service","docs/concepts/autonomy/typical_autonomous_navigation_use_case","docs/concepts/base_services","docs/concepts/bddf","docs/concepts/choreography/README","docs/concepts/choreography/animation_file_specification","docs/concepts/choreography/animations_in_choreographer","docs/concepts/choreography/choreographer","docs/concepts/choreography/choreographer_setup","docs/concepts/choreography/choreography_service","docs/concepts/choreography/move_reference","docs/concepts/choreography/robot_controls_in_choreographer","docs/concepts/data","docs/concepts/data_acquisition_output","docs/concepts/data_acquisition_overview","docs/concepts/data_buffer_overview","docs/concepts/developing_api_services","docs/concepts/estop_service","docs/concepts/faults","docs/concepts/geometry_and_frames","docs/concepts/lease_service","docs/concepts/network_compute_bridge","docs/concepts/networking","docs/concepts/robot_services","docs/concepts/writing_services_for_data_acquisition","docs/payload/README","docs/payload/configuring_payload_software","docs/payload/docker_containers","docs/payload/guidelines_for_robust_payload_design","docs/payload/mechanical_interfaces","docs/payload/payload_configuration_requirements","docs/payload/robot_electrical_interface","docs/payload/robot_mounting_rails","docs/payload/spot_core_cockpit","docs/payload/spot_core_vnc","docs/protos/README","docs/protos/style_guide","docs/python/README","docs/python/fetch_tutorial/fetch1","docs/python/fetch_tutorial/fetch2","docs/python/fetch_tutorial/fetch3","docs/python/fetch_tutorial/fetch4","docs/python/fetch_tutorial/fetch5","docs/python/quickstart","docs/python/understanding_spot_programming","docs/release_notes","protos/bosdyn/api/README","protos/bosdyn/api/proto_reference","python/README","python/bosdyn-choreography-client/src/bosdyn/choreography/client/README","python/bosdyn-choreography-client/src/bosdyn/choreography/client/animation_file_conversion_helpers","python/bosdyn-choreography-client/src/bosdyn/choreography/client/animation_file_to_proto","python/bosdyn-choreography-client/src/bosdyn/choreography/client/choreography","python/bosdyn-client/src/bosdyn/client/README","python/bosdyn-client/src/bosdyn/client/arm_surface_contact","python/bosdyn-client/src/bosdyn/client/async_tasks","python/bosdyn-client/src/bosdyn/client/auth","python/bosdyn-client/src/bosdyn/client/auto_return","python/bosdyn-client/src/bosdyn/client/bddf","python/bosdyn-client/src/bosdyn/client/bddf_download","python/bosdyn-client/src/bosdyn/client/channel","python/bosdyn-client/src/bosdyn/client/command_line","python/bosdyn-client/src/bosdyn/client/common","python/bosdyn-client/src/bosdyn/client/data_acquisition","python/bosdyn-client/src/bosdyn/client/data_acquisition_helpers","python/bosdyn-client/src/bosdyn/client/data_acquisition_plugin","python/bosdyn-client/src/bosdyn/client/data_acquisition_plugin_service","python/bosdyn-client/src/bosdyn/client/data_acquisition_store","python/bosdyn-client/src/bosdyn/client/data_buffer","python/bosdyn-client/src/bosdyn/client/data_service","python/bosdyn-client/src/bosdyn/client/directory","python/bosdyn-client/src/bosdyn/client/directory_registration","python/bosdyn-client/src/bosdyn/client/docking","python/bosdyn-client/src/bosdyn/client/door","python/bosdyn-client/src/bosdyn/client/estop","python/bosdyn-client/src/bosdyn/client/exceptions","python/bosdyn-client/src/bosdyn/client/fault","python/bosdyn-client/src/bosdyn/client/frame_helpers","python/bosdyn-client/src/bosdyn/client/graph_nav","python/bosdyn-client/src/bosdyn/client/image","python/bosdyn-client/src/bosdyn/client/image_service_helpers","python/bosdyn-client/src/bosdyn/client/ir_enable_disable","python/bosdyn-client/src/bosdyn/client/lease","python/bosdyn-client/src/bosdyn/client/license","python/bosdyn-client/src/bosdyn/client/local_grid","python/bosdyn-client/src/bosdyn/client/log_annotation","python/bosdyn-client/src/bosdyn/client/manipulation_api_client","python/bosdyn-client/src/bosdyn/client/map_processing","python/bosdyn-client/src/bosdyn/client/math_helpers","python/bosdyn-client/src/bosdyn/client/network_compute_bridge_client","python/bosdyn-client/src/bosdyn/client/payload","python/bosdyn-client/src/bosdyn/client/payload_registration","python/bosdyn-client/src/bosdyn/client/point_cloud","python/bosdyn-client/src/bosdyn/client/power","python/bosdyn-client/src/bosdyn/client/processors","python/bosdyn-client/src/bosdyn/client/recording","python/bosdyn-client/src/bosdyn/client/robot","python/bosdyn-client/src/bosdyn/client/robot_command","python/bosdyn-client/src/bosdyn/client/robot_id","python/bosdyn-client/src/bosdyn/client/robot_state","python/bosdyn-client/src/bosdyn/client/sdk","python/bosdyn-client/src/bosdyn/client/server_util","python/bosdyn-client/src/bosdyn/client/spot_cam/README","python/bosdyn-client/src/bosdyn/client/spot_cam/audio","python/bosdyn-client/src/bosdyn/client/spot_cam/compositor","python/bosdyn-client/src/bosdyn/client/spot_cam/health","python/bosdyn-client/src/bosdyn/client/spot_cam/lighting","python/bosdyn-client/src/bosdyn/client/spot_cam/media_log","python/bosdyn-client/src/bosdyn/client/spot_cam/network","python/bosdyn-client/src/bosdyn/client/spot_cam/power","python/bosdyn-client/src/bosdyn/client/spot_cam/ptz","python/bosdyn-client/src/bosdyn/client/spot_cam/streamquality","python/bosdyn-client/src/bosdyn/client/spot_cam/version","python/bosdyn-client/src/bosdyn/client/spot_check","python/bosdyn-client/src/bosdyn/client/time_sync","python/bosdyn-client/src/bosdyn/client/token_cache","python/bosdyn-client/src/bosdyn/client/token_manager","python/bosdyn-client/src/bosdyn/client/util","python/bosdyn-client/src/bosdyn/client/world_object","python/bosdyn-core/src/bosdyn/README","python/bosdyn-core/src/bosdyn/bddf/README","python/bosdyn-core/src/bosdyn/bddf/base_data_reader","python/bosdyn-core/src/bosdyn/bddf/block_writer","python/bosdyn-core/src/bosdyn/bddf/bosdyn","python/bosdyn-core/src/bosdyn/bddf/common","python/bosdyn-core/src/bosdyn/bddf/data_reader","python/bosdyn-core/src/bosdyn/bddf/data_writer","python/bosdyn-core/src/bosdyn/bddf/file_indexer","python/bosdyn-core/src/bosdyn/bddf/grpc_proto_reader","python/bosdyn-core/src/bosdyn/bddf/grpc_reader","python/bosdyn-core/src/bosdyn/bddf/grpc_service_reader","python/bosdyn-core/src/bosdyn/bddf/grpc_service_writer","python/bosdyn-core/src/bosdyn/bddf/message_reader","python/bosdyn-core/src/bosdyn/bddf/pod_series_reader","python/bosdyn-core/src/bosdyn/bddf/pod_series_writer","python/bosdyn-core/src/bosdyn/bddf/protobuf_channel_reader","python/bosdyn-core/src/bosdyn/bddf/protobuf_reader","python/bosdyn-core/src/bosdyn/bddf/protobuf_series_writer","python/bosdyn-core/src/bosdyn/bddf/stream_data_reader","python/bosdyn-core/src/bosdyn/geometry","python/bosdyn-core/src/bosdyn/util","python/bosdyn-mission/src/bosdyn/mission/README","python/bosdyn-mission/src/bosdyn/mission/client","python/bosdyn-mission/src/bosdyn/mission/constants","python/bosdyn-mission/src/bosdyn/mission/exceptions","python/bosdyn-mission/src/bosdyn/mission/remote_client","python/bosdyn-mission/src/bosdyn/mission/server_util","python/bosdyn-mission/src/bosdyn/mission/util","python/examples/README","python/examples/animation_recorder/README","python/examples/arm_and_mobility_command/README","python/examples/arm_constrained_manipulation/README","python/examples/arm_door/README","python/examples/arm_force_control/README","python/examples/arm_gaze/README","python/examples/arm_gcode/README","python/examples/arm_grasp/README","python/examples/arm_joint_move/README","python/examples/arm_simple/README","python/examples/arm_stow_unstow/README","python/examples/arm_surface_contact/README","python/examples/arm_trajectory/README","python/examples/arm_walk_to_object/README","python/examples/arm_with_body_follow/README","python/examples/auto_return/README","python/examples/bddf_download/README","python/examples/cloud_upload/README","python/examples/comms_test/README","python/examples/data_acquisition_service/README","python/examples/data_buffer/README","python/examples/data_service/README","python/examples/directory/README","python/examples/disable_ir_emission/README","python/examples/docking/README","python/examples/docs/arm_examples","python/examples/docs/autonomy_and_missions_examples","python/examples/docs/basic_service_examples","python/examples/docs/data_acquisition_examples","python/examples/docs/logging_examples","python/examples/docs/payloads_examples","python/examples/docs/perception_world_objects_examples","python/examples/docs/robot_behavior_examples","python/examples/estop/README","python/examples/fiducial_follow/README","python/examples/frame_trajectory_command/README","python/examples/get_depth_plus_visual_image/README","python/examples/get_image/README","python/examples/get_mission_state/README","python/examples/get_robot_state/README","python/examples/get_robot_state_async/README","python/examples/get_world_objects/README","python/examples/graph_nav_anchoring_optimization/README","python/examples/graph_nav_command_line/README","python/examples/graph_nav_extract_point_cloud/README","python/examples/graph_nav_view_map/README","python/examples/hello_spot/README","python/examples/logging/README","python/examples/mission_question_answerer/README","python/examples/mission_recorder/README","python/examples/network_compute_bridge/README","python/examples/network_compute_bridge/fire_extinguisher_server/README","python/examples/payloads/README","python/examples/post_docking_callbacks/README","python/examples/remote_mission_service/README","python/examples/replay_mission/README","python/examples/ricoh_theta/README","python/examples/self_registration/README","python/examples/service_faults/README","python/examples/spot_cam/README","python/examples/spot_detect_and_follow/README","python/examples/spot_light/README","python/examples/spot_tensorflow_detector/README","python/examples/stance/README","python/examples/stitch_front_images/README","python/examples/tester_programs/README","python/examples/time_sync/README","python/examples/upload_choreographed_sequence/README","python/examples/velodyne_client/README","python/examples/visualizer/README","python/examples/wasd/README","python/examples/web_cam_image_service/README","python/examples/world_object_mutations/README","python/examples/world_object_with_image_coordinates/README","python/examples/xbox_controller/README"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":3,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":2,"sphinx.domains.rst":2,"sphinx.domains.std":1,"sphinx.ext.intersphinx":1,"sphinx.ext.viewcode":1,sphinx:56},filenames:["README.md","choreography_protos/bosdyn/api/README.md","choreography_protos/bosdyn/api/choreography_reference.md","docs/concepts/README.md","docs/concepts/about_spot.md","docs/concepts/arm/README.md","docs/concepts/arm/arm_concepts.md","docs/concepts/arm/arm_services.md","docs/concepts/arm/arm_specification.md","docs/concepts/autonomy/README.md","docs/concepts/autonomy/auto_return.md","docs/concepts/autonomy/autonomous_navigation_code_examples.md","docs/concepts/autonomy/autonomous_navigation_services.md","docs/concepts/autonomy/components_of_autonomous_navigation.md","docs/concepts/autonomy/directed_exploration.md","docs/concepts/autonomy/docking.md","docs/concepts/autonomy/graphnav_and_robot_locomotion.md","docs/concepts/autonomy/graphnav_map_structure.md","docs/concepts/autonomy/graphnav_service.md","docs/concepts/autonomy/graphnav_tech_summary.md","docs/concepts/autonomy/initialization.md","docs/concepts/autonomy/localization.md","docs/concepts/autonomy/missions_service.md","docs/concepts/autonomy/typical_autonomous_navigation_use_case.md","docs/concepts/base_services.md","docs/concepts/bddf.md","docs/concepts/choreography/README.md","docs/concepts/choreography/animation_file_specification.md","docs/concepts/choreography/animations_in_choreographer.md","docs/concepts/choreography/choreographer.md","docs/concepts/choreography/choreographer_setup.md","docs/concepts/choreography/choreography_service.md","docs/concepts/choreography/move_reference.md","docs/concepts/choreography/robot_controls_in_choreographer.md","docs/concepts/data.md","docs/concepts/data_acquisition_output.md","docs/concepts/data_acquisition_overview.md","docs/concepts/data_buffer_overview.md","docs/concepts/developing_api_services.md","docs/concepts/estop_service.md","docs/concepts/faults.md","docs/concepts/geometry_and_frames.md","docs/concepts/lease_service.md","docs/concepts/network_compute_bridge.md","docs/concepts/networking.md","docs/concepts/robot_services.md","docs/concepts/writing_services_for_data_acquisition.md","docs/payload/README.md","docs/payload/configuring_payload_software.md","docs/payload/docker_containers.md","docs/payload/guidelines_for_robust_payload_design.md","docs/payload/mechanical_interfaces.md","docs/payload/payload_configuration_requirements.md","docs/payload/robot_electrical_interface.md","docs/payload/robot_mounting_rails.md","docs/payload/spot_core_cockpit.md","docs/payload/spot_core_vnc.md","docs/protos/README.md","docs/protos/style_guide.md","docs/python/README.md","docs/python/fetch_tutorial/fetch1.md","docs/python/fetch_tutorial/fetch2.md","docs/python/fetch_tutorial/fetch3.md","docs/python/fetch_tutorial/fetch4.md","docs/python/fetch_tutorial/fetch5.md","docs/python/quickstart.md","docs/python/understanding_spot_programming.md","docs/release_notes.md","protos/bosdyn/api/README.md","protos/bosdyn/api/proto_reference.md","python/README.md","python/bosdyn-choreography-client/src/bosdyn/choreography/client/README.md","python/bosdyn-choreography-client/src/bosdyn/choreography/client/animation_file_conversion_helpers.rst","python/bosdyn-choreography-client/src/bosdyn/choreography/client/animation_file_to_proto.rst","python/bosdyn-choreography-client/src/bosdyn/choreography/client/choreography.rst","python/bosdyn-client/src/bosdyn/client/README.md","python/bosdyn-client/src/bosdyn/client/arm_surface_contact.rst","python/bosdyn-client/src/bosdyn/client/async_tasks.rst","python/bosdyn-client/src/bosdyn/client/auth.rst","python/bosdyn-client/src/bosdyn/client/auto_return.rst","python/bosdyn-client/src/bosdyn/client/bddf.rst","python/bosdyn-client/src/bosdyn/client/bddf_download.rst","python/bosdyn-client/src/bosdyn/client/channel.rst","python/bosdyn-client/src/bosdyn/client/command_line.rst","python/bosdyn-client/src/bosdyn/client/common.rst","python/bosdyn-client/src/bosdyn/client/data_acquisition.rst","python/bosdyn-client/src/bosdyn/client/data_acquisition_helpers.rst","python/bosdyn-client/src/bosdyn/client/data_acquisition_plugin.rst","python/bosdyn-client/src/bosdyn/client/data_acquisition_plugin_service.rst","python/bosdyn-client/src/bosdyn/client/data_acquisition_store.rst","python/bosdyn-client/src/bosdyn/client/data_buffer.rst","python/bosdyn-client/src/bosdyn/client/data_service.rst","python/bosdyn-client/src/bosdyn/client/directory.rst","python/bosdyn-client/src/bosdyn/client/directory_registration.rst","python/bosdyn-client/src/bosdyn/client/docking.rst","python/bosdyn-client/src/bosdyn/client/door.rst","python/bosdyn-client/src/bosdyn/client/estop.rst","python/bosdyn-client/src/bosdyn/client/exceptions.rst","python/bosdyn-client/src/bosdyn/client/fault.rst","python/bosdyn-client/src/bosdyn/client/frame_helpers.rst","python/bosdyn-client/src/bosdyn/client/graph_nav.rst","python/bosdyn-client/src/bosdyn/client/image.rst","python/bosdyn-client/src/bosdyn/client/image_service_helpers.rst","python/bosdyn-client/src/bosdyn/client/ir_enable_disable.rst","python/bosdyn-client/src/bosdyn/client/lease.rst","python/bosdyn-client/src/bosdyn/client/license.rst","python/bosdyn-client/src/bosdyn/client/local_grid.rst","python/bosdyn-client/src/bosdyn/client/log_annotation.rst","python/bosdyn-client/src/bosdyn/client/manipulation_api_client.rst","python/bosdyn-client/src/bosdyn/client/map_processing.rst","python/bosdyn-client/src/bosdyn/client/math_helpers.rst","python/bosdyn-client/src/bosdyn/client/network_compute_bridge_client.rst","python/bosdyn-client/src/bosdyn/client/payload.rst","python/bosdyn-client/src/bosdyn/client/payload_registration.rst","python/bosdyn-client/src/bosdyn/client/point_cloud.rst","python/bosdyn-client/src/bosdyn/client/power.rst","python/bosdyn-client/src/bosdyn/client/processors.rst","python/bosdyn-client/src/bosdyn/client/recording.rst","python/bosdyn-client/src/bosdyn/client/robot.rst","python/bosdyn-client/src/bosdyn/client/robot_command.rst","python/bosdyn-client/src/bosdyn/client/robot_id.rst","python/bosdyn-client/src/bosdyn/client/robot_state.rst","python/bosdyn-client/src/bosdyn/client/sdk.rst","python/bosdyn-client/src/bosdyn/client/server_util.rst","python/bosdyn-client/src/bosdyn/client/spot_cam/README.md","python/bosdyn-client/src/bosdyn/client/spot_cam/audio.rst","python/bosdyn-client/src/bosdyn/client/spot_cam/compositor.rst","python/bosdyn-client/src/bosdyn/client/spot_cam/health.rst","python/bosdyn-client/src/bosdyn/client/spot_cam/lighting.rst","python/bosdyn-client/src/bosdyn/client/spot_cam/media_log.rst","python/bosdyn-client/src/bosdyn/client/spot_cam/network.rst","python/bosdyn-client/src/bosdyn/client/spot_cam/power.rst","python/bosdyn-client/src/bosdyn/client/spot_cam/ptz.rst","python/bosdyn-client/src/bosdyn/client/spot_cam/streamquality.rst","python/bosdyn-client/src/bosdyn/client/spot_cam/version.rst","python/bosdyn-client/src/bosdyn/client/spot_check.rst","python/bosdyn-client/src/bosdyn/client/time_sync.rst","python/bosdyn-client/src/bosdyn/client/token_cache.rst","python/bosdyn-client/src/bosdyn/client/token_manager.rst","python/bosdyn-client/src/bosdyn/client/util.rst","python/bosdyn-client/src/bosdyn/client/world_object.rst","python/bosdyn-core/src/bosdyn/README.md","python/bosdyn-core/src/bosdyn/bddf/README.md","python/bosdyn-core/src/bosdyn/bddf/base_data_reader.rst","python/bosdyn-core/src/bosdyn/bddf/block_writer.rst","python/bosdyn-core/src/bosdyn/bddf/bosdyn.rst","python/bosdyn-core/src/bosdyn/bddf/common.rst","python/bosdyn-core/src/bosdyn/bddf/data_reader.rst","python/bosdyn-core/src/bosdyn/bddf/data_writer.rst","python/bosdyn-core/src/bosdyn/bddf/file_indexer.rst","python/bosdyn-core/src/bosdyn/bddf/grpc_proto_reader.rst","python/bosdyn-core/src/bosdyn/bddf/grpc_reader.rst","python/bosdyn-core/src/bosdyn/bddf/grpc_service_reader.rst","python/bosdyn-core/src/bosdyn/bddf/grpc_service_writer.rst","python/bosdyn-core/src/bosdyn/bddf/message_reader.rst","python/bosdyn-core/src/bosdyn/bddf/pod_series_reader.rst","python/bosdyn-core/src/bosdyn/bddf/pod_series_writer.rst","python/bosdyn-core/src/bosdyn/bddf/protobuf_channel_reader.rst","python/bosdyn-core/src/bosdyn/bddf/protobuf_reader.rst","python/bosdyn-core/src/bosdyn/bddf/protobuf_series_writer.rst","python/bosdyn-core/src/bosdyn/bddf/stream_data_reader.rst","python/bosdyn-core/src/bosdyn/geometry.rst","python/bosdyn-core/src/bosdyn/util.rst","python/bosdyn-mission/src/bosdyn/mission/README.md","python/bosdyn-mission/src/bosdyn/mission/client.rst","python/bosdyn-mission/src/bosdyn/mission/constants.rst","python/bosdyn-mission/src/bosdyn/mission/exceptions.rst","python/bosdyn-mission/src/bosdyn/mission/remote_client.rst","python/bosdyn-mission/src/bosdyn/mission/server_util.rst","python/bosdyn-mission/src/bosdyn/mission/util.rst","python/examples/README.md","python/examples/animation_recorder/README.md","python/examples/arm_and_mobility_command/README.md","python/examples/arm_constrained_manipulation/README.md","python/examples/arm_door/README.md","python/examples/arm_force_control/README.md","python/examples/arm_gaze/README.md","python/examples/arm_gcode/README.md","python/examples/arm_grasp/README.md","python/examples/arm_joint_move/README.md","python/examples/arm_simple/README.md","python/examples/arm_stow_unstow/README.md","python/examples/arm_surface_contact/README.md","python/examples/arm_trajectory/README.md","python/examples/arm_walk_to_object/README.md","python/examples/arm_with_body_follow/README.md","python/examples/auto_return/README.md","python/examples/bddf_download/README.md","python/examples/cloud_upload/README.md","python/examples/comms_test/README.md","python/examples/data_acquisition_service/README.md","python/examples/data_buffer/README.md","python/examples/data_service/README.md","python/examples/directory/README.md","python/examples/disable_ir_emission/README.md","python/examples/docking/README.md","python/examples/docs/arm_examples.md","python/examples/docs/autonomy_and_missions_examples.md","python/examples/docs/basic_service_examples.md","python/examples/docs/data_acquisition_examples.md","python/examples/docs/logging_examples.md","python/examples/docs/payloads_examples.md","python/examples/docs/perception_world_objects_examples.md","python/examples/docs/robot_behavior_examples.md","python/examples/estop/README.md","python/examples/fiducial_follow/README.md","python/examples/frame_trajectory_command/README.md","python/examples/get_depth_plus_visual_image/README.md","python/examples/get_image/README.md","python/examples/get_mission_state/README.md","python/examples/get_robot_state/README.md","python/examples/get_robot_state_async/README.md","python/examples/get_world_objects/README.md","python/examples/graph_nav_anchoring_optimization/README.md","python/examples/graph_nav_command_line/README.md","python/examples/graph_nav_extract_point_cloud/README.md","python/examples/graph_nav_view_map/README.md","python/examples/hello_spot/README.md","python/examples/logging/README.md","python/examples/mission_question_answerer/README.md","python/examples/mission_recorder/README.md","python/examples/network_compute_bridge/README.md","python/examples/network_compute_bridge/fire_extinguisher_server/README.md","python/examples/payloads/README.md","python/examples/post_docking_callbacks/README.md","python/examples/remote_mission_service/README.md","python/examples/replay_mission/README.md","python/examples/ricoh_theta/README.md","python/examples/self_registration/README.md","python/examples/service_faults/README.md","python/examples/spot_cam/README.md","python/examples/spot_detect_and_follow/README.md","python/examples/spot_light/README.md","python/examples/spot_tensorflow_detector/README.md","python/examples/stance/README.md","python/examples/stitch_front_images/README.md","python/examples/tester_programs/README.md","python/examples/time_sync/README.md","python/examples/upload_choreographed_sequence/README.md","python/examples/velodyne_client/README.md","python/examples/visualizer/README.md","python/examples/wasd/README.md","python/examples/web_cam_image_service/README.md","python/examples/world_object_mutations/README.md","python/examples/world_object_with_image_coordinates/README.md","python/examples/xbox_controller/README.md"],objects:{"bosdyn.bddf":{base_data_reader:[143,0,0,"-"],block_writer:[144,0,0,"-"],bosdyn:[145,0,0,"-"],common:[146,0,0,"-"],data_reader:[147,0,0,"-"],data_writer:[148,0,0,"-"],file_indexer:[149,0,0,"-"],grpc_proto_reader:[150,0,0,"-"],grpc_reader:[151,0,0,"-"],grpc_service_reader:[152,0,0,"-"],grpc_service_writer:[153,0,0,"-"],message_reader:[154,0,0,"-"],pod_series_reader:[155,0,0,"-"],pod_series_writer:[156,0,0,"-"],protobuf_channel_reader:[157,0,0,"-"],protobuf_reader:[158,0,0,"-"],protobuf_series_writer:[159,0,0,"-"],stream_data_reader:[160,0,0,"-"]},"bosdyn.bddf.base_data_reader":{BaseDataReader:[143,1,1,""]},"bosdyn.bddf.base_data_reader.BaseDataReader":{annotations:[143,2,1,""],checksum:[143,2,1,""],file_descriptor:[143,2,1,""],file_index:[143,2,1,""],filename:[143,2,1,""],read_checksum:[143,2,1,""],series_spec_to_index:[143,2,1,""],version:[143,2,1,""]},"bosdyn.bddf.block_writer":{BlockWriter:[144,1,1,""]},"bosdyn.bddf.block_writer.BlockWriter":{close:[144,2,1,""],closed:[144,2,1,""],tell:[144,2,1,""],write_data_block:[144,2,1,""],write_descriptor_block:[144,2,1,""],write_file_end:[144,2,1,""],write_header:[144,2,1,""]},"bosdyn.bddf.bosdyn":{GrpcRequests:[145,1,1,""],GrpcResponses:[145,1,1,""],MessageChannel:[145,1,1,""],TypedMessageChannel:[145,1,1,""]},"bosdyn.bddf.bosdyn.GrpcRequests":{KEYS:[145,3,1,""],MESSAGE_TYPE:[145,3,1,""],SERIES_TYPE:[145,3,1,""],SERVICE_NAME:[145,3,1,""]},"bosdyn.bddf.bosdyn.GrpcResponses":{KEYS:[145,3,1,""],MESSAGE_TYPE:[145,3,1,""],SERIES_TYPE:[145,3,1,""],SERVICE_NAME:[145,3,1,""]},"bosdyn.bddf.bosdyn.MessageChannel":{CHANNEL:[145,3,1,""],KEYS:[145,3,1,""],SERIES_TYPE:[145,3,1,""]},"bosdyn.bddf.bosdyn.TypedMessageChannel":{CHANNEL:[145,3,1,""],KEYS:[145,3,1,""],MESSAGE_TYPE:[145,3,1,""],SERIES_TYPE:[145,3,1,""]},"bosdyn.bddf.common":{AddSeriesError:[146,4,1,""],ChecksumError:[146,4,1,""],DataError:[146,4,1,""],DataFormatError:[146,4,1,""],ParseError:[146,4,1,""],SeriesIdentifier:[146,1,1,""],SeriesNotUniqueError:[146,4,1,""]},"bosdyn.bddf.common.SeriesIdentifier":{KEYS:[146,3,1,""],SERIES_TYPE:[146,3,1,""]},"bosdyn.bddf.data_reader":{DataReader:[147,1,1,""]},"bosdyn.bddf.data_reader.DataReader":{num_data_blocks:[147,2,1,""],read:[147,2,1,""],series_block_index:[147,2,1,""],series_descriptor:[147,2,1,""],total_bytes:[147,2,1,""]},"bosdyn.bddf.data_writer":{DataWriter:[148,1,1,""]},"bosdyn.bddf.data_writer.DataWriter":{add_message_series:[148,2,1,""],add_pod_series:[148,2,1,""],add_series:[148,2,1,""],file_index:[148,2,1,""],run_on_close:[148,2,1,""],write_data:[148,2,1,""]},"bosdyn.bddf.file_indexer":{FileIndexer:[149,1,1,""]},"bosdyn.bddf.file_indexer.FileIndexer":{add_series:[149,2,1,""],add_series_descriptor:[149,2,1,""],descriptor_index:[149,2,1,""],file_index:[149,2,1,""],index_data_block:[149,2,1,""],make_data_descriptor:[149,2,1,""],series_block_indexes:[149,2,1,""],series_descriptor:[149,2,1,""],series_identifier_to_hash:[149,2,1,""],write_index:[149,2,1,""]},"bosdyn.bddf.grpc_proto_reader":{GrpcProtoReader:[150,1,1,""]},"bosdyn.bddf.grpc_proto_reader.GrpcProtoReader":{get_message:[150,2,1,""],num_messages:[150,2,1,""]},"bosdyn.bddf.grpc_reader":{GrpcReader:[151,1,1,""]},"bosdyn.bddf.grpc_reader.GrpcReader":{data_reader:[151,2,1,""],get_message:[151,2,1,""],get_proto_reader:[151,2,1,""]},"bosdyn.bddf.grpc_service_reader":{GrpcServiceReader:[152,1,1,""]},"bosdyn.bddf.grpc_service_reader.GrpcServiceReader":{add_proto_reader:[152,2,1,""],data_reader:[152,2,1,""],get_proto_reader:[152,2,1,""]},"bosdyn.bddf.grpc_service_writer":{GrpcServiceWriter:[153,1,1,""]},"bosdyn.bddf.grpc_service_writer.GrpcServiceWriter":{log_request:[153,2,1,""],log_response:[153,2,1,""]},"bosdyn.bddf.message_reader":{MessageReader:[154,1,1,""]},"bosdyn.bddf.message_reader.MessageReader":{channel_name_to_series_decriptor:[154,2,1,""],data_reader:[154,2,1,""],get_blob:[154,2,1,""],series_index:[154,2,1,""],series_index_to_descriptor:[154,2,1,""]},"bosdyn.bddf.pod_series_reader":{PodSeriesReader:[155,1,1,""]},"bosdyn.bddf.pod_series_reader.PodSeriesReader":{num_data_blocks:[155,2,1,""],pod_type:[155,2,1,""],read_samples:[155,2,1,""],series_descriptor:[155,2,1,""]},"bosdyn.bddf.pod_series_writer":{PodSeriesWriter:[156,1,1,""]},"bosdyn.bddf.pod_series_writer.PodSeriesWriter":{finish_block:[156,2,1,""],series_spec:[156,2,1,""],series_type:[156,2,1,""],write:[156,2,1,""]},"bosdyn.bddf.protobuf_channel_reader":{ProtobufChannelReader:[157,1,1,""]},"bosdyn.bddf.protobuf_channel_reader.ProtobufChannelReader":{Iterator:[157,1,1,""],get_message:[157,2,1,""],num_messages:[157,2,1,""],series_descriptor:[157,2,1,""]},"bosdyn.bddf.protobuf_reader":{ProtobufReader:[158,1,1,""]},"bosdyn.bddf.protobuf_reader.ProtobufReader":{get_message:[158,2,1,""]},"bosdyn.bddf.protobuf_series_writer":{ProtobufSeriesWriter:[159,1,1,""]},"bosdyn.bddf.protobuf_series_writer.ProtobufSeriesWriter":{series_spec:[159,2,1,""],series_type:[159,2,1,""],write:[159,2,1,""]},"bosdyn.bddf.stream_data_reader":{StreamDataReader:[160,1,1,""]},"bosdyn.bddf.stream_data_reader.StreamDataReader":{eof:[160,2,1,""],read_checksum:[160,2,1,""],read_data_block:[160,2,1,""],read_next_block:[160,2,1,""],series_block_index:[160,2,1,""],series_block_indexes:[160,2,1,""],series_descriptor:[160,2,1,""],stream_file_index:[160,2,1,""]},"bosdyn.choreography.client":{animation_file_conversion_helpers:[72,0,0,"-"],animation_file_to_proto:[73,0,0,"-"],choreography:[74,0,0,"-"]},"bosdyn.choreography.client.animation_file_conversion_helpers":{arm_joints_handler:[72,5,1,""],arm_playback_option:[72,5,1,""],arm_prohibited_option:[72,5,1,""],arm_required_option:[72,5,1,""],assume_zero_roll_and_pitch_option:[72,5,1,""],body_euler_rpy_angles_handler:[72,5,1,""],body_pitch_handler:[72,5,1,""],body_pos_handler:[72,5,1,""],body_quat_w_handler:[72,5,1,""],body_quat_x_handler:[72,5,1,""],body_quat_y_handler:[72,5,1,""],body_quat_z_handler:[72,5,1,""],body_quaternion_wxyz_handler:[72,5,1,""],body_quaternion_xyzw_handler:[72,5,1,""],body_roll_handler:[72,5,1,""],body_x_handler:[72,5,1,""],body_y_handler:[72,5,1,""],body_yaw_handler:[72,5,1,""],body_z_handler:[72,5,1,""],bpm_option:[72,5,1,""],com_pos_handler:[72,5,1,""],com_x_handler:[72,5,1,""],com_y_handler:[72,5,1,""],com_z_handler:[72,5,1,""],contact_handler:[72,5,1,""],controls_option:[72,5,1,""],description_option:[72,5,1,""],display_rgb_option:[72,5,1,""],el0_handler:[72,5,1,""],el1_handler:[72,5,1,""],extendable_option:[72,5,1,""],fl_angles_handler:[72,5,1,""],fl_contact_handler:[72,5,1,""],fl_hx_handler:[72,5,1,""],fl_hy_handler:[72,5,1,""],fl_kn_handler:[72,5,1,""],fl_pos_handler:[72,5,1,""],fl_x_handler:[72,5,1,""],fl_y_handler:[72,5,1,""],fl_z_handler:[72,5,1,""],foot_pos_handler:[72,5,1,""],fr_angles_handler:[72,5,1,""],fr_contact_handler:[72,5,1,""],fr_hx_handler:[72,5,1,""],fr_hy_handler:[72,5,1,""],fr_kn_handler:[72,5,1,""],fr_pos_handler:[72,5,1,""],fr_x_handler:[72,5,1,""],fr_y_handler:[72,5,1,""],fr_z_handler:[72,5,1,""],frequency_option:[72,5,1,""],gripper_handler:[72,5,1,""],hand_euler_rpy_angles_handler:[72,5,1,""],hand_pitch_handler:[72,5,1,""],hand_pos_handler:[72,5,1,""],hand_quat_w_handler:[72,5,1,""],hand_quat_x_handler:[72,5,1,""],hand_quat_y_handler:[72,5,1,""],hand_quat_z_handler:[72,5,1,""],hand_quaternion_wxyz_handler:[72,5,1,""],hand_quaternion_xyzw_handler:[72,5,1,""],hand_roll_handler:[72,5,1,""],hand_x_handler:[72,5,1,""],hand_y_handler:[72,5,1,""],hand_yaw_handler:[72,5,1,""],hand_z_handler:[72,5,1,""],hl_angles_handler:[72,5,1,""],hl_contact_handler:[72,5,1,""],hl_hx_handler:[72,5,1,""],hl_hy_handler:[72,5,1,""],hl_kn_handler:[72,5,1,""],hl_pos_handler:[72,5,1,""],hl_x_handler:[72,5,1,""],hl_y_handler:[72,5,1,""],hl_z_handler:[72,5,1,""],hr_angles_handler:[72,5,1,""],hr_contact_handler:[72,5,1,""],hr_hx_handler:[72,5,1,""],hr_hy_handler:[72,5,1,""],hr_kn_handler:[72,5,1,""],hr_pos_handler:[72,5,1,""],hr_x_handler:[72,5,1,""],hr_y_handler:[72,5,1,""],hr_z_handler:[72,5,1,""],leg_angles_handler:[72,5,1,""],neutral_start_option:[72,5,1,""],no_looping_option:[72,5,1,""],precise_steps_option:[72,5,1,""],precise_timing_option:[72,5,1,""],retime_to_integer_slices_option:[72,5,1,""],sh0_handler:[72,5,1,""],sh1_handler:[72,5,1,""],start_time_handler:[72,5,1,""],track_hand_rt_body_option:[72,5,1,""],track_hand_rt_feet_option:[72,5,1,""],track_swing_trajectories_option:[72,5,1,""],truncatable_option:[72,5,1,""],wr0_handler:[72,5,1,""],wr1_handler:[72,5,1,""]},"bosdyn.choreography.client.animation_file_to_proto":{Animation:[73,1,1,""],convert_animation_file_to_proto:[73,5,1,""],handle_nested_double_value_params:[73,5,1,""],main:[73,5,1,""],read_and_find_animation_params:[73,5,1,""],read_animation_params:[73,5,1,""],set_proto:[73,5,1,""],write_animation_to_dest:[73,5,1,""]},"bosdyn.choreography.client.animation_file_to_proto.Animation":{create_move_info_proto:[73,2,1,""]},"bosdyn.choreography.client.choreography":{AnimationUploadHelper:[74,1,1,""],AnimationValidationFailedError:[74,4,1,""],ChoreographyClient:[74,1,1,""],IncompleteData:[74,4,1,""],InvalidUploadedChoreographyError:[74,4,1,""],LeaseError:[74,4,1,""],NoRecordedInformation:[74,4,1,""],RecordingBufferFull:[74,4,1,""],RobotCommandIssuesError:[74,4,1,""],UnknownRecordingSessionId:[74,4,1,""],load_choreography_sequence_from_binary_file:[74,5,1,""],load_choreography_sequence_from_txt_file:[74,5,1,""],save_choreography_sequence_to_file:[74,5,1,""]},"bosdyn.choreography.client.choreography.AnimationUploadHelper":{ANIMATION_MOVE_PREFIX:[74,3,1,""],generate_animation_id:[74,2,1,""],initialize:[74,2,1,""],upload_animated_move:[74,2,1,""]},"bosdyn.choreography.client.choreography.ChoreographyClient":{build_execute_choreography_request:[74,2,1,""],build_start_recording_state_request:[74,2,1,""],choreography_log_to_animation_file:[74,2,1,""],default_service_name:[74,3,1,""],download_robot_state_log:[74,2,1,""],execute_choreography:[74,2,1,""],execute_choreography_async:[74,2,1,""],list_all_moves:[74,2,1,""],list_all_moves_async:[74,2,1,""],service_type:[74,3,1,""],start_recording_state:[74,2,1,""],start_recording_state_async:[74,2,1,""],stop_recording_state:[74,2,1,""],stop_recording_state_async:[74,2,1,""],timesync_endpoint:[74,2,1,""],update_from:[74,2,1,""],upload_animated_move:[74,2,1,""],upload_animated_move_async:[74,2,1,""],upload_choreography:[74,2,1,""],upload_choreography_async:[74,2,1,""]},"bosdyn.client":{arm_surface_contact:[76,0,0,"-"],async_tasks:[77,0,0,"-"],auth:[78,0,0,"-"],auto_return:[79,0,0,"-"],bddf:[80,0,0,"-"],bddf_download:[81,0,0,"-"],channel:[82,0,0,"-"],command_line:[83,0,0,"-"],common:[84,0,0,"-"],data_acquisition:[85,0,0,"-"],data_acquisition_helpers:[86,0,0,"-"],data_acquisition_plugin:[87,0,0,"-"],data_acquisition_plugin_service:[88,0,0,"-"],data_acquisition_store:[89,0,0,"-"],data_buffer:[90,0,0,"-"],data_service:[91,0,0,"-"],directory:[92,0,0,"-"],directory_registration:[93,0,0,"-"],docking:[94,0,0,"-"],door:[95,0,0,"-"],estop:[96,0,0,"-"],exceptions:[97,0,0,"-"],fault:[98,0,0,"-"],frame_helpers:[99,0,0,"-"],graph_nav:[100,0,0,"-"],image:[101,0,0,"-"],image_service_helpers:[102,0,0,"-"],ir_enable_disable:[103,0,0,"-"],lease:[104,0,0,"-"],license:[105,0,0,"-"],local_grid:[106,0,0,"-"],log_annotation:[107,0,0,"-"],manipulation_api_client:[108,0,0,"-"],map_processing:[109,0,0,"-"],math_helpers:[110,0,0,"-"],network_compute_bridge_client:[111,0,0,"-"],payload:[112,0,0,"-"],payload_registration:[113,0,0,"-"],point_cloud:[114,0,0,"-"],power:[115,0,0,"-"],processors:[116,0,0,"-"],recording:[117,0,0,"-"],robot:[118,0,0,"-"],robot_command:[119,0,0,"-"],robot_id:[120,0,0,"-"],robot_state:[121,0,0,"-"],sdk:[122,0,0,"-"],server_util:[123,0,0,"-"],spot_check:[135,0,0,"-"],time_sync:[136,0,0,"-"],token_cache:[137,0,0,"-"],token_manager:[138,0,0,"-"],util:[139,0,0,"-"],world_object:[140,0,0,"-"]},"bosdyn.client.arm_surface_contact":{ArmSurfaceContactClient:[76,1,1,""]},"bosdyn.client.arm_surface_contact.ArmSurfaceContactClient":{arm_surface_contact_command:[76,2,1,""],arm_surface_contact_command_async:[76,2,1,""],default_service_name:[76,3,1,""],service_type:[76,3,1,""],update_from:[76,2,1,""]},"bosdyn.client.async_tasks":{AsyncGRPCTask:[77,1,1,""],AsyncPeriodicGRPCTask:[77,1,1,""],AsyncPeriodicQuery:[77,1,1,""],AsyncTasks:[77,1,1,""]},"bosdyn.client.async_tasks.AsyncGRPCTask":{update:[77,2,1,""]},"bosdyn.client.async_tasks.AsyncPeriodicQuery":{proto:[77,2,1,""]},"bosdyn.client.async_tasks.AsyncTasks":{add_task:[77,2,1,""],update:[77,2,1,""]},"bosdyn.client.auth":{AuthClient:[78,1,1,""],AuthResponseError:[78,4,1,""],ExpiredApplicationTokenError:[78,4,1,""],InvalidApplicationTokenError:[78,4,1,""],InvalidLoginError:[78,4,1,""],InvalidTokenError:[78,4,1,""],TemporarilyLockedOutError:[78,4,1,""]},"bosdyn.client.auth.AuthClient":{auth:[78,2,1,""],auth_async:[78,2,1,""],auth_with_token:[78,2,1,""],auth_with_token_async:[78,2,1,""],default_service_name:[78,3,1,""],service_type:[78,3,1,""]},"bosdyn.client.auto_return":{AutoReturnClient:[79,1,1,""],AutoReturnResponseError:[79,4,1,""],InvalidParameterError:[79,4,1,""],configure_error:[79,5,1,""]},"bosdyn.client.auto_return.AutoReturnClient":{configure:[79,2,1,""],configure_async:[79,2,1,""],default_service_name:[79,3,1,""],get_configuration:[79,2,1,""],get_configuration_async:[79,2,1,""],service_type:[79,3,1,""],start:[79,2,1,""],start_async:[79,2,1,""]},"bosdyn.client.bddf_download":{download_data:[81,5,1,""],main:[81,5,1,""]},"bosdyn.client.channel":{RefreshingAccessTokenAuthMetadataPlugin:[82,1,1,""],create_insecure_channel:[82,5,1,""],create_secure_channel:[82,5,1,""],create_secure_channel_creds:[82,5,1,""],generate_channel_options:[82,5,1,""],translate_exception:[82,5,1,""]},"bosdyn.client.command_line":{BecomeEstopCommand:[83,1,1,""],Command:[83,1,1,""],DataAcquisitionCommand:[83,1,1,""],DataAcquisitionRequestCommand:[83,1,1,""],DataAcquisitionServiceCommand:[83,1,1,""],DataAcquisitionStatusCommand:[83,1,1,""],DataBufferCommands:[83,1,1,""],DataServiceCommands:[83,1,1,""],DirectoryCommands:[83,1,1,""],DirectoryGetCommand:[83,1,1,""],DirectoryListCommand:[83,1,1,""],DirectoryRegisterCommand:[83,1,1,""],DirectoryUnregisterCommand:[83,1,1,""],FaultCommands:[83,1,1,""],FaultShowCommand:[83,1,1,""],FaultWatchCommand:[83,1,1,""],FullStateCommand:[83,1,1,""],GetDataBufferCommentsCommand:[83,1,1,""],GetDataBufferEventsCommand:[83,1,1,""],GetDataBufferEventsCommentsCommand:[83,1,1,""],GetDataBufferStatusCommand:[83,1,1,""],GetImageCommand:[83,1,1,""],GetLocalGridsCommand:[83,1,1,""],HardwareConfigurationCommand:[83,1,1,""],HostComputerIPCommand:[83,1,1,""],ImageCommands:[83,1,1,""],LeaseCommands:[83,1,1,""],LeaseListCommand:[83,1,1,""],LicenseCommand:[83,1,1,""],ListImageSourcesCommand:[83,1,1,""],ListLocalGridTypesCommand:[83,1,1,""],LocalGridCommands:[83,1,1,""],MetricsCommand:[83,1,1,""],OperatorCommentCommand:[83,1,1,""],PayloadCommands:[83,1,1,""],PayloadListCommand:[83,1,1,""],PayloadRegisterCommand:[83,1,1,""],PowerCommand:[83,1,1,""],PowerPayloadsCommand:[83,1,1,""],PowerRobotCommand:[83,1,1,""],PowerWifiRadioCommand:[83,1,1,""],RobotIdCommand:[83,1,1,""],RobotModel:[83,1,1,""],RobotStateCommands:[83,1,1,""],Subcommands:[83,1,1,""],TextMsgCommand:[83,1,1,""],TimeSyncCommand:[83,1,1,""],main:[83,5,1,""]},"bosdyn.client.command_line.BecomeEstopCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.Command":{NAME:[83,3,1,""],NEED_AUTHENTICATION:[83,3,1,""],run:[83,2,1,""]},"bosdyn.client.command_line.DataAcquisitionCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.DataAcquisitionRequestCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.DataAcquisitionServiceCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.DataAcquisitionStatusCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.DataBufferCommands":{NAME:[83,3,1,""]},"bosdyn.client.command_line.DataServiceCommands":{NAME:[83,3,1,""]},"bosdyn.client.command_line.DirectoryCommands":{NAME:[83,3,1,""]},"bosdyn.client.command_line.DirectoryGetCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.DirectoryListCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.DirectoryRegisterCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.DirectoryUnregisterCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.FaultCommands":{NAME:[83,3,1,""]},"bosdyn.client.command_line.FaultShowCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.FaultWatchCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.FullStateCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.GetDataBufferCommentsCommand":{NAME:[83,3,1,""],pretty_print:[83,2,1,""]},"bosdyn.client.command_line.GetDataBufferEventsCommand":{NAME:[83,3,1,""],pretty_print:[83,2,1,""]},"bosdyn.client.command_line.GetDataBufferEventsCommentsCommand":{pretty_print:[83,2,1,""]},"bosdyn.client.command_line.GetDataBufferStatusCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.GetImageCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.GetLocalGridsCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.HardwareConfigurationCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.HostComputerIPCommand":{NAME:[83,3,1,""],NEED_AUTHENTICATION:[83,3,1,""]},"bosdyn.client.command_line.ImageCommands":{NAME:[83,3,1,""]},"bosdyn.client.command_line.LeaseCommands":{NAME:[83,3,1,""]},"bosdyn.client.command_line.LeaseListCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.LicenseCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.ListImageSourcesCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.ListLocalGridTypesCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.LocalGridCommands":{NAME:[83,3,1,""]},"bosdyn.client.command_line.MetricsCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.OperatorCommentCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.PayloadCommands":{NAME:[83,3,1,""]},"bosdyn.client.command_line.PayloadListCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.PayloadRegisterCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.PowerCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.PowerPayloadsCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.PowerRobotCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.PowerWifiRadioCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.RobotIdCommand":{NAME:[83,3,1,""],NEED_AUTHENTICATION:[83,3,1,""]},"bosdyn.client.command_line.RobotModel":{NAME:[83,3,1,""],NEED_AUTHENTICATION:[83,3,1,""]},"bosdyn.client.command_line.RobotStateCommands":{NAME:[83,3,1,""]},"bosdyn.client.command_line.TextMsgCommand":{NAME:[83,3,1,""]},"bosdyn.client.command_line.TimeSyncCommand":{NAME:[83,3,1,""]},"bosdyn.client.common":{BaseClient:[84,1,1,""],FutureWrapper:[84,1,1,""],common_header_errors:[84,5,1,""],common_lease_errors:[84,5,1,""],error_factory:[84,5,1,""],error_pair:[84,5,1,""],get_self_ip:[84,5,1,""],handle_common_header_errors:[84,5,1,""],handle_lease_use_result_errors:[84,5,1,""],handle_unset_status_error:[84,5,1,""],maybe_raise:[84,5,1,""],print_response:[84,5,1,""],process_kwargs:[84,5,1,""],streaming_common_header_errors:[84,5,1,""],streaming_common_lease_errors:[84,5,1,""]},"bosdyn.client.common.BaseClient":{call:[84,2,1,""],call_async:[84,2,1,""],channel:[84,2,1,""],chunk_message:[84,2,1,""],handle_response:[84,2,1,""],handle_response_streaming:[84,2,1,""],request_trim_for_log:[84,2,1,""],response_trim_for_log:[84,2,1,""],update_from:[84,2,1,""],update_request_iterator:[84,2,1,""],update_response_iterator:[84,2,1,""]},"bosdyn.client.common.FutureWrapper":{add_done_callback:[84,2,1,""],cancel:[84,2,1,""],cancelled:[84,2,1,""],done:[84,2,1,""],exception:[84,2,1,""],result:[84,2,1,""],running:[84,2,1,""],traceback:[84,2,1,""]},"bosdyn.client.data_acquisition":{CancellationFailedError:[85,4,1,""],DataAcquisitionClient:[85,1,1,""],DataAcquisitionResponseError:[85,4,1,""],RequestIdDoesNotExistError:[85,4,1,""],UnknownCaptureTypeError:[85,4,1,""],acquire_data_error:[85,5,1,""],get_request_id:[85,5,1,""],metadata_to_proto:[85,5,1,""]},"bosdyn.client.data_acquisition.DataAcquisitionClient":{acquire_data:[85,2,1,""],acquire_data_async:[85,2,1,""],acquire_data_from_request:[85,2,1,""],acquire_data_from_request_async:[85,2,1,""],cancel_acquisition:[85,2,1,""],cancel_acquisition_async:[85,2,1,""],default_service_name:[85,3,1,""],get_service_info:[85,2,1,""],get_service_info_async:[85,2,1,""],get_status:[85,2,1,""],get_status_async:[85,2,1,""],make_acquire_data_request:[85,2,1,""],service_type:[85,3,1,""],update_from:[85,2,1,""]},"bosdyn.client.data_acquisition_helpers":{acquire_and_process_request:[86,5,1,""],cancel_acquisition_request:[86,5,1,""],clean_filename:[86,5,1,""],download_data_REST:[86,5,1,""],issue_acquire_data_request:[86,5,1,""],make_time_query_params:[86,5,1,""],make_time_query_params_from_group_name:[86,5,1,""]},"bosdyn.client.data_acquisition_plugin":{DataAcquisitionPluginClient:[87,1,1,""]},"bosdyn.client.data_acquisition_plugin.DataAcquisitionPluginClient":{acquire_plugin_data:[87,2,1,""],acquire_plugin_data_async:[87,2,1,""],cancel_acquisition:[87,2,1,""],cancel_acquisition_async:[87,2,1,""],default_service_name:[87,3,1,""],get_service_info:[87,2,1,""],get_service_info_async:[87,2,1,""],get_status:[87,2,1,""],get_status_async:[87,2,1,""],service_type:[87,3,1,""],update_from:[87,2,1,""]},"bosdyn.client.data_acquisition_plugin_service":{DataAcquisitionPluginService:[88,1,1,""],DataAcquisitionStoreHelper:[88,1,1,""],RequestCancelledError:[88,4,1,""],RequestManager:[88,1,1,""],RequestState:[88,1,1,""],make_error:[88,5,1,""]},"bosdyn.client.data_acquisition_plugin_service.DataAcquisitionPluginService":{AcquirePluginData:[88,2,1,""],CancelAcquisition:[88,2,1,""],GetServiceInfo:[88,2,1,""],GetStatus:[88,2,1,""],acquire_response_fn:[88,3,1,""],capabilities:[88,3,1,""],data_collect_fn:[88,3,1,""],executor:[88,3,1,""],logger:[88,3,1,""],request_manager:[88,3,1,""],robot:[88,3,1,""],service_type:[88,3,1,""],store_client:[88,3,1,""]},"bosdyn.client.data_acquisition_plugin_service.DataAcquisitionStoreHelper":{cancel_check:[88,2,1,""],cancel_interval:[88,3,1,""],data_id_future_pairs:[88,3,1,""],state:[88,3,1,""],store_client:[88,3,1,""],store_data:[88,2,1,""],store_image:[88,2,1,""],store_metadata:[88,2,1,""],wait_for_stores_complete:[88,2,1,""]},"bosdyn.client.data_acquisition_plugin_service.RequestManager":{add_request:[88,2,1,""],cleanup_requests:[88,2,1,""],get_request_state:[88,2,1,""],get_status_proto:[88,2,1,""],mark_request_cancelled:[88,2,1,""],mark_request_finished:[88,2,1,""]},"bosdyn.client.data_acquisition_plugin_service.RequestState":{add_errors:[88,2,1,""],add_saved:[88,2,1,""],cancel_check:[88,2,1,""],has_data_errors:[88,2,1,""],is_cancelled:[88,2,1,""],kNonError:[88,3,1,""],set_complete_if_no_error:[88,2,1,""],set_status:[88,2,1,""]},"bosdyn.client.data_acquisition_store":{DataAcquisitionStoreClient:[89,1,1,""]},"bosdyn.client.data_acquisition_store.DataAcquisitionStoreClient":{default_service_name:[89,3,1,""],list_capture_actions:[89,2,1,""],list_capture_actions_async:[89,2,1,""],list_stored_data:[89,2,1,""],list_stored_data_async:[89,2,1,""],list_stored_images:[89,2,1,""],list_stored_images_async:[89,2,1,""],list_stored_metadata:[89,2,1,""],list_stored_metadata_async:[89,2,1,""],service_type:[89,3,1,""],store_data:[89,2,1,""],store_data_async:[89,2,1,""],store_image:[89,2,1,""],store_image_async:[89,2,1,""],store_metadata:[89,2,1,""],store_metadata_async:[89,2,1,""],update_from:[89,2,1,""]},"bosdyn.client.data_buffer":{DataBufferClient:[90,1,1,""],InvalidArgument:[90,4,1,""],log_event:[90,5,1,""]},"bosdyn.client.data_buffer.DataBufferClient":{add_blob:[90,2,1,""],add_blob_async:[90,2,1,""],add_events:[90,2,1,""],add_events_async:[90,2,1,""],add_operator_comment:[90,2,1,""],add_operator_comment_async:[90,2,1,""],add_protobuf:[90,2,1,""],add_protobuf_async:[90,2,1,""],add_signal_tick:[90,2,1,""],add_signal_tick_async:[90,2,1,""],add_text_messages:[90,2,1,""],add_text_messages_async:[90,2,1,""],default_service_name:[90,3,1,""],register_signal_schema:[90,2,1,""],register_signal_schema_async:[90,2,1,""],service_type:[90,3,1,""],update_from:[90,2,1,""]},"bosdyn.client.data_service":{DataServiceClient:[91,1,1,""],InvalidArgument:[91,4,1,""]},"bosdyn.client.data_service.DataServiceClient":{default_service_name:[91,3,1,""],delete_data_pages:[91,2,1,""],delete_data_pages_async:[91,2,1,""],get_data_buffer_status:[91,2,1,""],get_data_buffer_status_async:[91,2,1,""],get_data_index:[91,2,1,""],get_data_index_async:[91,2,1,""],get_data_pages:[91,2,1,""],get_data_pages_async:[91,2,1,""],get_events_comments:[91,2,1,""],get_events_comments_async:[91,2,1,""],service_type:[91,3,1,""],update_from:[91,2,1,""]},"bosdyn.client.directory":{DirectoryClient:[92,1,1,""],DirectoryResponseError:[92,4,1,""],NonexistentServiceError:[92,4,1,""]},"bosdyn.client.directory.DirectoryClient":{default_service_name:[92,3,1,""],get_entry:[92,2,1,""],get_entry_async:[92,2,1,""],list:[92,2,1,""],list_async:[92,2,1,""],service_type:[92,3,1,""]},"bosdyn.client.directory_registration":{DirectoryRegistrationClient:[93,1,1,""],DirectoryRegistrationKeepAlive:[93,1,1,""],DirectoryRegistrationResponseError:[93,4,1,""],ServiceAlreadyExistsError:[93,4,1,""],ServiceDoesNotExistError:[93,4,1,""],reset_service_registration:[93,5,1,""]},"bosdyn.client.directory_registration.DirectoryRegistrationClient":{default_service_name:[93,3,1,""],register:[93,2,1,""],service_type:[93,3,1,""],unregister:[93,2,1,""],update:[93,2,1,""]},"bosdyn.client.directory_registration.DirectoryRegistrationKeepAlive":{is_alive:[93,2,1,""],shutdown:[93,2,1,""],start:[93,2,1,""],unregister:[93,2,1,""]},"bosdyn.client.docking":{DockingClient:[94,1,1,""],blocking_dock_robot:[94,5,1,""],blocking_go_to_prep_pose:[94,5,1,""],blocking_undock:[94,5,1,""]},"bosdyn.client.docking.DockingClient":{default_service_name:[94,3,1,""],docking_command:[94,2,1,""],docking_command_async:[94,2,1,""],docking_command_feedback:[94,2,1,""],docking_command_feedback_async:[94,2,1,""],docking_command_feedback_full:[94,2,1,""],docking_command_feedback_full_async:[94,2,1,""],docking_command_full:[94,2,1,""],docking_command_full_async:[94,2,1,""],get_docking_config:[94,2,1,""],get_docking_config_async:[94,2,1,""],get_docking_state:[94,2,1,""],get_docking_state_async:[94,2,1,""],service_type:[94,3,1,""],update_from:[94,2,1,""]},"bosdyn.client.door":{DoorClient:[95,1,1,""]},"bosdyn.client.door.DoorClient":{default_service_name:[95,3,1,""],open_door:[95,2,1,""],open_door_async:[95,2,1,""],open_door_feedback:[95,2,1,""],open_door_feedback_async:[95,2,1,""],service_type:[95,3,1,""],update_from:[95,2,1,""]},"bosdyn.client.estop":{ConfigMismatchError:[96,4,1,""],EndpointMismatchError:[96,4,1,""],EndpointUnknownError:[96,4,1,""],EstopClient:[96,1,1,""],EstopEndpoint:[96,1,1,""],EstopKeepAlive:[96,1,1,""],EstopResponseError:[96,4,1,""],IncorrectChallengeResponseError:[96,4,1,""],InvalidEndpointError:[96,4,1,""],InvalidIdError:[96,4,1,""],MotorsOnError:[96,4,1,""],StopLevel:[96,1,1,""],is_estopped:[96,5,1,""],response_from_challenge:[96,5,1,""]},"bosdyn.client.estop.EstopClient":{check_in:[96,2,1,""],check_in_async:[96,2,1,""],default_service_name:[96,3,1,""],deregister:[96,2,1,""],deregister_async:[96,2,1,""],get_config:[96,2,1,""],get_config_async:[96,2,1,""],get_status:[96,2,1,""],get_status_async:[96,2,1,""],register:[96,2,1,""],register_async:[96,2,1,""],service_type:[96,3,1,""],set_config:[96,2,1,""],set_config_async:[96,2,1,""]},"bosdyn.client.estop.EstopEndpoint":{REQUIRED_ROLE:[96,3,1,""],allow:[96,2,1,""],allow_async:[96,2,1,""],check_in_at_level:[96,2,1,""],check_in_at_level_async:[96,2,1,""],deregister:[96,2,1,""],deregister_async:[96,2,1,""],force_simple_setup:[96,2,1,""],from_proto:[96,2,1,""],get_challenge:[96,2,1,""],register:[96,2,1,""],set_challenge:[96,2,1,""],settle_then_cut:[96,2,1,""],settle_then_cut_async:[96,2,1,""],stop:[96,2,1,""],stop_async:[96,2,1,""],to_proto:[96,2,1,""],unique_id:[96,2,1,""]},"bosdyn.client.estop.EstopKeepAlive":{KeepAliveStatus:[96,1,1,""],allow:[96,2,1,""],client:[96,2,1,""],endpoint:[96,2,1,""],logger:[96,2,1,""],settle_then_cut:[96,2,1,""],shutdown:[96,2,1,""],stop:[96,2,1,""]},"bosdyn.client.estop.EstopKeepAlive.KeepAliveStatus":{DISABLED:[96,3,1,""],ERROR:[96,3,1,""],OK:[96,3,1,""]},"bosdyn.client.estop.StopLevel":{ESTOP_LEVEL_CUT:[96,3,1,""],ESTOP_LEVEL_NONE:[96,3,1,""],ESTOP_LEVEL_SETTLE_THEN_CUT:[96,3,1,""],ESTOP_LEVEL_UNKNOWN:[96,3,1,""]},"bosdyn.client.exceptions":{ClientCancelledOperationError:[97,4,1,""],Error:[97,4,1,""],InternalServerError:[97,4,1,""],InvalidAppTokenError:[97,4,1,""],InvalidClientCertificateError:[97,4,1,""],InvalidRequestError:[97,4,1,""],LeaseUseError:[97,4,1,""],LicenseError:[97,4,1,""],NonexistentAuthorityError:[97,4,1,""],NotFoundError:[97,4,1,""],PermissionDeniedError:[97,4,1,""],PersistentRpcError:[97,4,1,""],ProxyConnectionError:[97,4,1,""],ResponseError:[97,4,1,""],ResponseTooLargeError:[97,4,1,""],RetryableRpcError:[97,4,1,""],RetryableUnavailableError:[97,4,1,""],RpcError:[97,4,1,""],ServerError:[97,4,1,""],ServiceFailedDuringExecutionError:[97,4,1,""],ServiceUnavailableError:[97,4,1,""],TimeSyncRequired:[97,4,1,""],TimedOutError:[97,4,1,""],TooManyRequestsError:[97,4,1,""],TransientFailureError:[97,4,1,""],UnableToConnectToRobotError:[97,4,1,""],UnauthenticatedError:[97,4,1,""],UnimplementedError:[97,4,1,""],UnknownDnsNameError:[97,4,1,""],UnsetStatusError:[97,4,1,""]},"bosdyn.client.fault":{FaultClient:[98,1,1,""],FaultResponseError:[98,4,1,""],ServiceFaultAlreadyExistsError:[98,4,1,""],ServiceFaultDoesNotExistError:[98,4,1,""]},"bosdyn.client.fault.FaultClient":{clear_service_fault:[98,2,1,""],clear_service_fault_async:[98,2,1,""],default_service_name:[98,3,1,""],service_type:[98,3,1,""],trigger_service_fault:[98,2,1,""],trigger_service_fault_async:[98,2,1,""]},"bosdyn.client.frame_helpers":{ChildFrameInTree:[99,4,1,""],Error:[99,4,1,""],GenerateTreeError:[99,4,1,""],ValidateFrameTreeCycleError:[99,4,1,""],ValidateFrameTreeDisjointError:[99,4,1,""],ValidateFrameTreeError:[99,4,1,""],ValidateFrameTreeUnknownFrameError:[99,4,1,""],add_edge_to_tree:[99,5,1,""],express_se2_velocity_in_new_frame:[99,5,1,""],express_se3_velocity_in_new_frame:[99,5,1,""],get_a_tform_b:[99,5,1,""],get_frame_names:[99,5,1,""],get_odom_tform_body:[99,5,1,""],get_se2_a_tform_b:[99,5,1,""],get_vision_tform_body:[99,5,1,""],is_gravity_aligned_frame_name:[99,5,1,""],validate_frame_tree_snapshot:[99,5,1,""]},"bosdyn.client.graph_nav":{CommandExpiredError:[100,4,1,""],ConstraintFaultError:[100,4,1,""],FeatureDesertError:[100,4,1,""],GraphNavClient:[100,1,1,""],GraphNavServiceResponseError:[100,4,1,""],InvalidEdgeError:[100,4,1,""],InvalidGraphError:[100,4,1,""],InvalidPoseError:[100,4,1,""],IsRecordingError:[100,4,1,""],MapTooLargeLicenseError:[100,4,1,""],NoAnchoringError:[100,4,1,""],NoPathError:[100,4,1,""],NoTimeSyncError:[100,4,1,""],RequestAbortedError:[100,4,1,""],RequestFailedError:[100,4,1,""],RobotFaultedError:[100,4,1,""],RobotImpairedError:[100,4,1,""],RobotLostError:[100,4,1,""],RobotNotLocalizedToRouteError:[100,4,1,""],RobotStateError:[100,4,1,""],RobotStuckError:[100,4,1,""],RouteError:[100,4,1,""],RouteNavigationError:[100,4,1,""],RouteNotUpdatingError:[100,4,1,""],TimeError:[100,4,1,""],TooDistantError:[100,4,1,""],UnknownMapInformationError:[100,4,1,""],UnknownRouteElementsError:[100,4,1,""],UnknownWaypointError:[100,4,1,""],UnkownRouteElementsError:[100,4,1,""],UnrecongizedCommandError:[100,4,1,""],UploadGraphError:[100,4,1,""]},"bosdyn.client.graph_nav.GraphNavClient":{build_route:[100,2,1,""],clear_graph:[100,2,1,""],clear_graph_async:[100,2,1,""],default_service_name:[100,3,1,""],download_edge_snapshot:[100,2,1,""],download_graph:[100,2,1,""],download_graph_async:[100,2,1,""],download_waypoint_snapshot:[100,2,1,""],generate_route_params:[100,2,1,""],generate_travel_params:[100,2,1,""],get_localization_state:[100,2,1,""],get_localization_state_async:[100,2,1,""],navigate_route:[100,2,1,""],navigate_route_async:[100,2,1,""],navigate_route_full:[100,2,1,""],navigate_route_full_async:[100,2,1,""],navigate_to:[100,2,1,""],navigate_to_anchor:[100,2,1,""],navigate_to_anchor_async:[100,2,1,""],navigate_to_async:[100,2,1,""],navigate_to_full:[100,2,1,""],navigate_to_full_async:[100,2,1,""],navigation_feedback:[100,2,1,""],navigation_feedback_async:[100,2,1,""],service_type:[100,3,1,""],set_localization:[100,2,1,""],set_localization_async:[100,2,1,""],update_from:[100,2,1,""],upload_edge_snapshot:[100,2,1,""],upload_graph:[100,2,1,""],upload_graph_async:[100,2,1,""],upload_waypoint_snapshot:[100,2,1,""],write_graph_and_snapshots:[100,2,1,""]},"bosdyn.client.image":{ImageClient:[101,1,1,""],ImageDataError:[101,4,1,""],ImageResponseError:[101,4,1,""],SourceDataError:[101,4,1,""],UnknownImageSourceError:[101,4,1,""],UnsupportedImageFormatRequestedError:[101,4,1,""],build_image_request:[101,5,1,""],pixel_to_camera_space:[101,5,1,""],save_images_as_files:[101,5,1,""],write_image_data:[101,5,1,""],write_pgm_or_ppm:[101,5,1,""]},"bosdyn.client.image.ImageClient":{default_service_name:[101,3,1,""],get_image:[101,2,1,""],get_image_async:[101,2,1,""],get_image_from_sources:[101,2,1,""],get_image_from_sources_async:[101,2,1,""],list_image_sources:[101,2,1,""],list_image_sources_async:[101,2,1,""],service_type:[101,3,1,""]},"bosdyn.client.image_service_helpers":{CameraBaseImageServicer:[102,1,1,""],CameraInterface:[102,1,1,""],ImageCaptureThread:[102,1,1,""],VisualImageSource:[102,1,1,""]},"bosdyn.client.image_service_helpers.CameraBaseImageServicer":{GetImage:[102,2,1,""],ListImageSources:[102,2,1,""]},"bosdyn.client.image_service_helpers.CameraInterface":{blocking_capture:[102,2,1,""],image_decode:[102,2,1,""]},"bosdyn.client.image_service_helpers.ImageCaptureThread":{get_latest_captured_image:[102,2,1,""],set_last_captured_image:[102,2,1,""],start_capturing:[102,2,1,""],stop_capturing:[102,2,1,""]},"bosdyn.client.image_service_helpers.VisualImageSource":{clear_fault:[102,2,1,""],create_capture_thread:[102,2,1,""],get_image_and_timestamp:[102,2,1,""],image_decode_with_error_checking:[102,2,1,""],initialize_faults:[102,2,1,""],make_capture_parameters:[102,2,1,""],make_image_source:[102,2,1,""],set_logger:[102,2,1,""],stop_capturing:[102,2,1,""],trigger_fault:[102,2,1,""]},"bosdyn.client.ir_enable_disable":{IREnableDisableServiceClient:[103,1,1,""]},"bosdyn.client.ir_enable_disable.IREnableDisableServiceClient":{default_service_name:[103,3,1,""],service_type:[103,3,1,""],set_ir_enabled:[103,2,1,""],set_ir_enabled_async:[103,2,1,""]},"bosdyn.client.lease":{DisplacedLeaseError:[104,4,1,""],Error:[104,4,1,""],InvalidLeaseError:[104,4,1,""],InvalidResourceError:[104,4,1,""],Lease:[104,1,1,""],LeaseClient:[104,1,1,""],LeaseKeepAlive:[104,1,1,""],LeaseNotOwnedByWallet:[104,4,1,""],LeaseResponseError:[104,4,1,""],LeaseState:[104,1,1,""],LeaseWallet:[104,1,1,""],LeaseWalletRequestProcessor:[104,1,1,""],LeaseWalletResponseProcessor:[104,1,1,""],NoSuchLease:[104,4,1,""],NotActiveLeaseError:[104,4,1,""],NotAuthoritativeServiceError:[104,4,1,""],ResourceAlreadyClaimedError:[104,4,1,""],RevokedLeaseError:[104,4,1,""],UnmanagedResourceError:[104,4,1,""],WrongEpochError:[104,4,1,""],add_lease_wallet_processors:[104,5,1,""],test_active_lease:[104,5,1,""]},"bosdyn.client.lease.Lease":{CompareResult:[104,1,1,""],compare:[104,2,1,""],compare_result_to_lease_use_result_status:[104,2,1,""],create_newer:[104,2,1,""],create_sublease:[104,2,1,""],is_valid_lease:[104,2,1,""],is_valid_proto:[104,2,1,""]},"bosdyn.client.lease.Lease.CompareResult":{DIFFERENT_EPOCHS:[104,3,1,""],DIFFERENT_RESOURCES:[104,3,1,""],NEWER:[104,3,1,""],OLDER:[104,3,1,""],SAME:[104,3,1,""],SUB_LEASE:[104,3,1,""],SUPER_LEASE:[104,3,1,""]},"bosdyn.client.lease.LeaseClient":{acquire:[104,2,1,""],acquire_async:[104,2,1,""],default_service_name:[104,3,1,""],list_leases:[104,2,1,""],list_leases_async:[104,2,1,""],list_leases_full:[104,2,1,""],list_leases_full_async:[104,2,1,""],retain_lease:[104,2,1,""],retain_lease_async:[104,2,1,""],return_lease:[104,2,1,""],return_lease_async:[104,2,1,""],service_type:[104,3,1,""],take:[104,2,1,""],take_async:[104,2,1,""]},"bosdyn.client.lease.LeaseKeepAlive":{is_alive:[104,2,1,""],lease_wallet:[104,2,1,""],shutdown:[104,2,1,""],wait_until_done:[104,2,1,""]},"bosdyn.client.lease.LeaseState":{STATUS_NOT_MANAGED:[104,3,1,""],STATUS_OTHER_OWNER:[104,3,1,""],STATUS_REVOKED:[104,3,1,""],STATUS_SELF_OWNER:[104,3,1,""],STATUS_UNOWNED:[104,3,1,""],Status:[104,1,1,""],create_newer:[104,2,1,""],update_from_lease_use_result:[104,2,1,""]},"bosdyn.client.lease.LeaseState.Status":{NOT_MANAGED:[104,3,1,""],OTHER_OWNER:[104,3,1,""],REVOKED:[104,3,1,""],SELF_OWNER:[104,3,1,""],UNOWNED:[104,3,1,""]},"bosdyn.client.lease.LeaseWallet":{add:[104,2,1,""],advance:[104,2,1,""],get_lease:[104,2,1,""],get_lease_state:[104,2,1,""],on_lease_use_result:[104,2,1,""],remove:[104,2,1,""],set_client_name:[104,2,1,""]},"bosdyn.client.lease.LeaseWalletRequestProcessor":{get_lease_state:[104,2,1,""],mutate:[104,2,1,""]},"bosdyn.client.lease.LeaseWalletResponseProcessor":{mutate:[104,2,1,""]},"bosdyn.client.license":{LicenseClient:[105,1,1,""]},"bosdyn.client.license.LicenseClient":{default_service_name:[105,3,1,""],get_feature_enabled:[105,2,1,""],get_license_info:[105,2,1,""],service_type:[105,3,1,""]},"bosdyn.client.local_grid":{LocalGridClient:[106,1,1,""]},"bosdyn.client.local_grid.LocalGridClient":{default_service_name:[106,3,1,""],get_local_grid_types:[106,2,1,""],get_local_grid_types_async:[106,2,1,""],get_local_grids:[106,2,1,""],get_local_grids_async:[106,2,1,""],service_type:[106,3,1,""]},"bosdyn.client.log_annotation":{InvalidArgument:[107,4,1,""],LogAnnotationClient:[107,1,1,""],LogAnnotationHandler:[107,1,1,""]},"bosdyn.client.log_annotation.LogAnnotationClient":{add_log_blob:[107,2,1,""],add_log_blob_async:[107,2,1,""],add_log_protobuf:[107,2,1,""],add_log_protobuf_async:[107,2,1,""],add_operator_comment:[107,2,1,""],add_operator_comment_async:[107,2,1,""],add_text_messages:[107,2,1,""],add_text_messages_async:[107,2,1,""],default_service_name:[107,3,1,""],service_type:[107,3,1,""],update_from:[107,2,1,""]},"bosdyn.client.log_annotation.LogAnnotationHandler":{close:[107,2,1,""],emit:[107,2,1,""],fallback_log:[107,2,1,""],flush:[107,2,1,""],is_thread_alive:[107,2,1,""],record_level_to_proto_level:[107,2,1,""],record_to_msg:[107,2,1,""],restart:[107,2,1,""]},"bosdyn.client.manipulation_api_client":{ManipulationApiClient:[108,1,1,""]},"bosdyn.client.manipulation_api_client.ManipulationApiClient":{default_service_name:[108,3,1,""],grasp_override_command:[108,2,1,""],grasp_override_command_async:[108,2,1,""],manipulation_api_command:[108,2,1,""],manipulation_api_command_async:[108,2,1,""],manipulation_api_feedback_command:[108,2,1,""],manipulation_api_feedback_command_async:[108,2,1,""],service_type:[108,3,1,""],update_from:[108,2,1,""]},"bosdyn.client.map_processing":{ConstraintViolationError:[109,4,1,""],InvalidGraphError:[109,4,1,""],InvalidHintsError:[109,4,1,""],InvalidParamsError:[109,4,1,""],MapModifiedError:[109,4,1,""],MapProcessingServiceClient:[109,1,1,""],MapProcessingServiceResponseError:[109,4,1,""],MaxIterationsError:[109,4,1,""],MaxTimeError:[109,4,1,""],MissingSnapshotsError:[109,4,1,""],OptimizationFailureError:[109,4,1,""]},"bosdyn.client.map_processing.MapProcessingServiceClient":{default_service_name:[109,3,1,""],process_anchoring:[109,2,1,""],process_topology:[109,2,1,""],service_type:[109,3,1,""]},"bosdyn.client.math_helpers":{Quat:[110,1,1,""],SE2Pose:[110,1,1,""],SE2Velocity:[110,1,1,""],SE3Pose:[110,1,1,""],SE3Velocity:[110,1,1,""],angle_diff:[110,5,1,""],angle_diff_degrees:[110,5,1,""],is_within_threshold:[110,5,1,""],pose_to_xyz_yaw:[110,5,1,""],quat_to_eulerZYX:[110,5,1,""],recenter_angle:[110,5,1,""],skew_matrix_2d:[110,5,1,""],skew_matrix_3d:[110,5,1,""],transform_se2velocity:[110,5,1,""],transform_se3velocity:[110,5,1,""]},"bosdyn.client.math_helpers.Quat":{closest_yaw_only_quaternion:[110,2,1,""],from_matrix:[110,2,1,""],from_obj:[110,2,1,""],from_pitch:[110,2,1,""],from_roll:[110,2,1,""],from_yaw:[110,2,1,""],inverse:[110,2,1,""],mult:[110,2,1,""],normalize:[110,2,1,""],slerp:[110,2,1,""],to_axis_angle:[110,2,1,""],to_matrix:[110,2,1,""],to_obj:[110,2,1,""],to_pitch:[110,2,1,""],to_proto:[110,2,1,""],to_roll:[110,2,1,""],to_yaw:[110,2,1,""],transform_point:[110,2,1,""],transform_vec3:[110,2,1,""]},"bosdyn.client.math_helpers.SE2Pose":{flatten:[110,2,1,""],from_matrix:[110,2,1,""],from_obj:[110,2,1,""],get_closest_se3_transform:[110,2,1,""],inverse:[110,2,1,""],mult:[110,2,1,""],position:[110,2,1,""],to_adjoint_matrix:[110,2,1,""],to_matrix:[110,2,1,""],to_obj:[110,2,1,""],to_proto:[110,2,1,""],to_rot_matrix:[110,2,1,""]},"bosdyn.client.math_helpers.SE2Velocity":{angular:[110,2,1,""],from_obj:[110,2,1,""],from_vector:[110,2,1,""],linear:[110,2,1,""],to_obj:[110,2,1,""],to_proto:[110,2,1,""],to_vector:[110,2,1,""]},"bosdyn.client.math_helpers.SE3Pose":{from_identity:[110,2,1,""],from_matrix:[110,2,1,""],from_obj:[110,2,1,""],from_se2:[110,2,1,""],get_closest_se2_transform:[110,2,1,""],get_translation:[110,2,1,""],interp:[110,2,1,""],inverse:[110,2,1,""],mult:[110,2,1,""],position:[110,2,1,""],rotation:[110,2,1,""],to_adjoint_matrix:[110,2,1,""],to_matrix:[110,2,1,""],to_obj:[110,2,1,""],to_proto:[110,2,1,""],transform_cloud:[110,2,1,""],transform_cloud_from_matrix:[110,2,1,""],transform_point:[110,2,1,""],transform_vec3:[110,2,1,""]},"bosdyn.client.math_helpers.SE3Velocity":{angular:[110,2,1,""],from_obj:[110,2,1,""],from_vector:[110,2,1,""],linear:[110,2,1,""],to_obj:[110,2,1,""],to_proto:[110,2,1,""],to_vector:[110,2,1,""]},"bosdyn.client.network_compute_bridge_client":{ExternalServerError:[111,4,1,""],ExternalServiceNotFoundError:[111,4,1,""],NetworkComputeBridgeClient:[111,1,1,""],NetworkComputeRotationError:[111,4,1,""]},"bosdyn.client.network_compute_bridge_client.NetworkComputeBridgeClient":{default_service_name:[111,3,1,""],list_available_models_command:[111,2,1,""],list_available_models_command_async:[111,2,1,""],network_compute_bridge_command:[111,2,1,""],network_compute_bridge_command_async:[111,2,1,""],service_type:[111,3,1,""]},"bosdyn.client.payload":{PayloadClient:[112,1,1,""]},"bosdyn.client.payload.PayloadClient":{default_service_name:[112,3,1,""],list_payloads:[112,2,1,""],list_payloads_async:[112,2,1,""],service_type:[112,3,1,""]},"bosdyn.client.payload_registration":{InvalidPayloadCredentialsError:[113,4,1,""],PayloadAlreadyExistsError:[113,4,1,""],PayloadDoesNotExistError:[113,4,1,""],PayloadNotAuthorizedError:[113,4,1,""],PayloadRegistrationClient:[113,1,1,""],PayloadRegistrationKeepAlive:[113,1,1,""],PayloadRegistrationResponseError:[113,4,1,""]},"bosdyn.client.payload_registration.PayloadRegistrationClient":{attach_payload:[113,2,1,""],attach_payload_async:[113,2,1,""],default_service_name:[113,3,1,""],detach_payload:[113,2,1,""],detach_payload_async:[113,2,1,""],get_payload_auth_token:[113,2,1,""],register_payload:[113,2,1,""],register_payload_async:[113,2,1,""],service_type:[113,3,1,""],update_payload_version:[113,2,1,""],update_payload_version_async:[113,2,1,""]},"bosdyn.client.payload_registration.PayloadRegistrationKeepAlive":{is_alive:[113,2,1,""],shutdown:[113,2,1,""],start:[113,2,1,""]},"bosdyn.client.point_cloud":{PointCloudClient:[114,1,1,""],PointCloudDataError:[114,4,1,""],PointCloudResponseError:[114,4,1,""],SourceDataError:[114,4,1,""],UnknownPointCloudSourceError:[114,4,1,""],build_pc_request:[114,5,1,""]},"bosdyn.client.point_cloud.PointCloudClient":{default_service_name:[114,3,1,""],get_point_cloud:[114,2,1,""],get_point_cloud_async:[114,2,1,""],get_point_cloud_from_sources:[114,2,1,""],get_point_cloud_from_sources_async:[114,2,1,""],list_point_cloud_sources:[114,2,1,""],list_point_cloud_sources_async:[114,2,1,""],service_type:[114,3,1,""]},"bosdyn.client.power":{BatteryMissingError:[115,4,1,""],CommandInProgressError:[115,4,1,""],CommandTimedOutError:[115,4,1,""],EstoppedError:[115,4,1,""],FaultedError:[115,4,1,""],OverriddenError:[115,4,1,""],PowerClient:[115,1,1,""],PowerError:[115,4,1,""],PowerResponseError:[115,4,1,""],ShorePowerConnectedError:[115,4,1,""],is_powered_on:[115,5,1,""],power_cycle_robot:[115,5,1,""],power_off:[115,5,1,""],power_off_motors:[115,5,1,""],power_off_payload_ports:[115,5,1,""],power_off_robot:[115,5,1,""],power_off_wifi_radio:[115,5,1,""],power_on:[115,5,1,""],power_on_motors:[115,5,1,""],power_on_payload_ports:[115,5,1,""],power_on_wifi_radio:[115,5,1,""],safe_power_cycle_robot:[115,5,1,""],safe_power_off:[115,5,1,""],safe_power_off_motors:[115,5,1,""],safe_power_off_robot:[115,5,1,""]},"bosdyn.client.power.PowerClient":{default_service_name:[115,3,1,""],power_command:[115,2,1,""],power_command_async:[115,2,1,""],power_command_feedback:[115,2,1,""],power_command_feedback_async:[115,2,1,""],service_type:[115,3,1,""],update_from:[115,2,1,""]},"bosdyn.client.processors":{AddRequestHeader:[116,1,1,""]},"bosdyn.client.processors.AddRequestHeader":{mutate:[116,2,1,""]},"bosdyn.client.recording":{CouldNotCreateWaypointError:[117,4,1,""],EdgeExistsError:[117,4,1,""],EdgeMissingTransformError:[117,4,1,""],FiducialPoseError:[117,4,1,""],FollowingRouteError:[117,4,1,""],GraphNavRecordingServiceClient:[117,1,1,""],MapTooLargeLicenseError:[117,4,1,""],MissingFiducialsError:[117,4,1,""],NotLocalizedToEndError:[117,4,1,""],NotLocalizedToExistingMapError:[117,4,1,""],NotReadyYetError:[117,4,1,""],NotRecordingError:[117,4,1,""],RecordingServiceResponseError:[117,4,1,""],RemoteCloudFailureNoDataError:[117,4,1,""],RemoteCloudFailureNotInDirectoryError:[117,4,1,""],TooFarFromExistingMapError:[117,4,1,""],UnknownWaypointError:[117,4,1,""],WaypointRegion:[117,1,1,""]},"bosdyn.client.recording.GraphNavRecordingServiceClient":{create_edge:[117,2,1,""],create_edge_async:[117,2,1,""],create_waypoint:[117,2,1,""],create_waypoint_async:[117,2,1,""],default_service_name:[117,3,1,""],get_record_status:[117,2,1,""],get_record_status_async:[117,2,1,""],make_edge:[117,2,1,""],make_edge_environment:[117,2,1,""],make_recording_environment:[117,2,1,""],make_waypoint_environment:[117,2,1,""],service_type:[117,3,1,""],set_recording_environment:[117,2,1,""],set_recording_environment_async:[117,2,1,""],start_recording:[117,2,1,""],start_recording_async:[117,2,1,""],stop_recording:[117,2,1,""],stop_recording_async:[117,2,1,""]},"bosdyn.client.recording.WaypointRegion":{CIRCLE_REGION:[117,3,1,""],DEFAULT_REGION:[117,3,1,""],EMPTY_REGION:[117,3,1,""]},"bosdyn.client.robot":{Robot:[118,1,1,""],RobotError:[118,4,1,""],UnregisteredServiceError:[118,4,1,""],UnregisteredServiceNameError:[118,4,1,""],UnregisteredServiceTypeError:[118,4,1,""]},"bosdyn.client.robot.Robot":{authenticate:[118,2,1,""],authenticate_from_cache:[118,2,1,""],authenticate_from_payload_credentials:[118,2,1,""],authenticate_with_token:[118,2,1,""],ensure_channel:[118,2,1,""],ensure_client:[118,2,1,""],ensure_secure_channel:[118,2,1,""],get_cached_robot_id:[118,2,1,""],get_cached_usernames:[118,2,1,""],get_frame_tree_snapshot:[118,2,1,""],get_id:[118,2,1,""],has_arm:[118,2,1,""],is_estopped:[118,2,1,""],is_powered_on:[118,2,1,""],list_services:[118,2,1,""],log_event:[118,2,1,""],operator_comment:[118,2,1,""],power_off:[118,2,1,""],power_on:[118,2,1,""],register_payload_and_authenticate:[118,2,1,""],setup_token_cache:[118,2,1,""],start_time_sync:[118,2,1,""],stop_time_sync:[118,2,1,""],sync_with_directory:[118,2,1,""],time_sec:[118,2,1,""],time_sync:[118,2,1,""],update_from:[118,2,1,""],update_user_token:[118,2,1,""]},"bosdyn.client.robot_command":{BehaviorFaultError:[119,4,1,""],CommandFailedError:[119,4,1,""],CommandTimedOutError:[119,4,1,""],Error:[119,4,1,""],ExpiredError:[119,4,1,""],NoTimeSyncError:[119,4,1,""],NotClearedError:[119,4,1,""],NotPoweredOnError:[119,4,1,""],RobotCommandBuilder:[119,1,1,""],RobotCommandClient:[119,1,1,""],RobotCommandResponseError:[119,4,1,""],TooDistantError:[119,4,1,""],UnknownFrameError:[119,4,1,""],UnsupportedError:[119,4,1,""],block_until_arm_arrives:[119,5,1,""],blocking_stand:[119,5,1,""]},"bosdyn.client.robot_command.RobotCommandBuilder":{arm_carry_command:[119,2,1,""],arm_gaze_command:[119,2,1,""],arm_joint_command:[119,2,1,""],arm_pose_command:[119,2,1,""],arm_ready_command:[119,2,1,""],arm_stow_command:[119,2,1,""],arm_wrench_command:[119,2,1,""],battery_change_pose_command:[119,2,1,""],build_body_external_forces:[119,2,1,""],build_synchro_command:[119,2,1,""],claw_gripper_close_command:[119,2,1,""],claw_gripper_open_angle_command:[119,2,1,""],claw_gripper_open_command:[119,2,1,""],claw_gripper_open_fraction_command:[119,2,1,""],constrained_manipulation_command:[119,2,1,""],create_arm_joint_trajectory_point:[119,2,1,""],follow_arm_command:[119,2,1,""],freeze_command:[119,2,1,""],mobility_params:[119,2,1,""],safe_power_off_command:[119,2,1,""],selfright_command:[119,2,1,""],sit_command:[119,2,1,""],stance_command:[119,2,1,""],stand_command:[119,2,1,""],stop_command:[119,2,1,""],synchro_se2_trajectory_command:[119,2,1,""],synchro_se2_trajectory_point_command:[119,2,1,""],synchro_sit_command:[119,2,1,""],synchro_stand_command:[119,2,1,""],synchro_trajectory_command_in_body_frame:[119,2,1,""],synchro_velocity_command:[119,2,1,""],trajectory_command:[119,2,1,""],velocity_command:[119,2,1,""]},"bosdyn.client.robot_command.RobotCommandClient":{clear_behavior_fault:[119,2,1,""],clear_behavior_fault_async:[119,2,1,""],default_service_name:[119,3,1,""],robot_command:[119,2,1,""],robot_command_async:[119,2,1,""],robot_command_feedback:[119,2,1,""],robot_command_feedback_async:[119,2,1,""],service_type:[119,3,1,""],timesync_endpoint:[119,2,1,""],update_from:[119,2,1,""]},"bosdyn.client.robot_id":{RobotIdClient:[120,1,1,""],create_strict_version:[120,5,1,""]},"bosdyn.client.robot_id.RobotIdClient":{default_service_name:[120,3,1,""],get_id:[120,2,1,""],get_id_async:[120,2,1,""],service_type:[120,3,1,""]},"bosdyn.client.robot_state":{RobotStateClient:[121,1,1,""],has_arm:[121,5,1,""]},"bosdyn.client.robot_state.RobotStateClient":{default_service_name:[121,3,1,""],get_hardware_config_with_link_info:[121,2,1,""],get_robot_hardware_configuration:[121,2,1,""],get_robot_hardware_configuration_async:[121,2,1,""],get_robot_link_model:[121,2,1,""],get_robot_link_model_async:[121,2,1,""],get_robot_metrics:[121,2,1,""],get_robot_metrics_async:[121,2,1,""],get_robot_state:[121,2,1,""],get_robot_state_async:[121,2,1,""],service_type:[121,3,1,""]},"bosdyn.client.sdk":{Sdk:[122,1,1,""],SdkError:[122,4,1,""],UnableToLoadAppTokenError:[122,4,1,""],UnsetAppTokenError:[122,4,1,""],create_standard_sdk:[122,5,1,""],decode_token:[122,5,1,""],generate_client_name:[122,5,1,""],log_token_time_remaining:[122,5,1,""]},"bosdyn.client.sdk.Sdk":{clear_robots:[122,2,1,""],create_robot:[122,2,1,""],load_app_token:[122,2,1,""],load_robot_cert:[122,2,1,""],register_service_client:[122,2,1,""],set_max_message_length:[122,2,1,""]},"bosdyn.client.server_util":{GrpcServiceRunner:[123,1,1,""],ResponseContext:[123,1,1,""],get_bytes_field_allowlist:[123,5,1,""],populate_response_header:[123,5,1,""],strip_get_image_response:[123,5,1,""],strip_image_response:[123,5,1,""],strip_large_bytes_fields:[123,5,1,""],strip_local_grid_responses:[123,5,1,""],strip_log_annotation:[123,5,1,""],strip_record_data_blob:[123,5,1,""],strip_record_signal_tick:[123,5,1,""],strip_store_data_request:[123,5,1,""],strip_store_image_request:[123,5,1,""]},"bosdyn.client.server_util.GrpcServiceRunner":{run_until_interrupt:[123,2,1,""],stop:[123,2,1,""]},"bosdyn.client.spot_cam":{audio:[125,0,0,"-"],compositor:[126,0,0,"-"],health:[127,0,0,"-"],lighting:[128,0,0,"-"],media_log:[129,0,0,"-"],network:[130,0,0,"-"],power:[131,0,0,"-"],ptz:[132,0,0,"-"],streamquality:[133,0,0,"-"],version:[134,0,0,"-"]},"bosdyn.client.spot_cam.audio":{AudioClient:[125,1,1,""]},"bosdyn.client.spot_cam.audio.AudioClient":{default_service_name:[125,3,1,""],delete_sound:[125,2,1,""],delete_sound_async:[125,2,1,""],get_audio_capture_channel:[125,2,1,""],get_audio_capture_channel_async:[125,2,1,""],get_audio_capture_gain:[125,2,1,""],get_audio_capture_gain_async:[125,2,1,""],get_volume:[125,2,1,""],get_volume_async:[125,2,1,""],list_sounds:[125,2,1,""],list_sounds_async:[125,2,1,""],load_sound:[125,2,1,""],play_sound:[125,2,1,""],play_sound_async:[125,2,1,""],service_type:[125,3,1,""],set_audio_capture_channel:[125,2,1,""],set_audio_capture_channel_async:[125,2,1,""],set_audio_capture_gain:[125,2,1,""],set_audio_capture_gain_async:[125,2,1,""],set_volume:[125,2,1,""],set_volume_async:[125,2,1,""]},"bosdyn.client.spot_cam.compositor":{CompositorClient:[126,1,1,""]},"bosdyn.client.spot_cam.compositor.CompositorClient":{default_service_name:[126,3,1,""],get_ir_colormap:[126,2,1,""],get_ir_colormap_async:[126,2,1,""],get_screen:[126,2,1,""],get_screen_async:[126,2,1,""],get_visible_cameras:[126,2,1,""],get_visible_cameras_async:[126,2,1,""],list_screens:[126,2,1,""],list_screens_async:[126,2,1,""],service_type:[126,3,1,""],set_ir_colormap:[126,2,1,""],set_ir_colormap_async:[126,2,1,""],set_ir_meter_overlay:[126,2,1,""],set_ir_meter_overlay_async:[126,2,1,""],set_screen:[126,2,1,""],set_screen_async:[126,2,1,""]},"bosdyn.client.spot_cam.health":{HealthClient:[127,1,1,""]},"bosdyn.client.spot_cam.health.HealthClient":{clear_bit_events:[127,2,1,""],clear_bit_events_async:[127,2,1,""],default_service_name:[127,3,1,""],get_bit_status:[127,2,1,""],get_bit_status_async:[127,2,1,""],get_temperature:[127,2,1,""],get_temperature_async:[127,2,1,""],service_type:[127,3,1,""]},"bosdyn.client.spot_cam.lighting":{LightingClient:[128,1,1,""]},"bosdyn.client.spot_cam.lighting.LightingClient":{default_service_name:[128,3,1,""],get_led_brightness:[128,2,1,""],get_led_brightness_async:[128,2,1,""],service_type:[128,3,1,""],set_led_brightness:[128,2,1,""],set_led_brightness_async:[128,2,1,""]},"bosdyn.client.spot_cam.media_log":{MediaLogClient:[129,1,1,""]},"bosdyn.client.spot_cam.media_log.MediaLogClient":{"delete":[129,2,1,""],default_service_name:[129,3,1,""],delete_async:[129,2,1,""],enable_debug:[129,2,1,""],enable_debug_async:[129,2,1,""],get_status:[129,2,1,""],get_status_async:[129,2,1,""],list_cameras:[129,2,1,""],list_cameras_async:[129,2,1,""],list_logpoints:[129,2,1,""],retrieve:[129,2,1,""],retrieve_raw_data:[129,2,1,""],service_type:[129,3,1,""],set_passphrase:[129,2,1,""],set_passphrase_async:[129,2,1,""],store:[129,2,1,""],store_async:[129,2,1,""],tag:[129,2,1,""],tag_async:[129,2,1,""]},"bosdyn.client.spot_cam.network":{NetworkClient:[130,1,1,""]},"bosdyn.client.spot_cam.network.NetworkClient":{default_service_name:[130,3,1,""],get_ice_configuration:[130,2,1,""],get_ice_configuration_async:[130,2,1,""],service_type:[130,3,1,""],set_ice_configuration:[130,2,1,""],set_ice_configuration_async:[130,2,1,""]},"bosdyn.client.spot_cam.power":{PowerClient:[131,1,1,""]},"bosdyn.client.spot_cam.power.PowerClient":{cycle_power:[131,2,1,""],cycle_power_async:[131,2,1,""],default_service_name:[131,3,1,""],get_power_status:[131,2,1,""],get_power_status_async:[131,2,1,""],service_type:[131,3,1,""],set_power_status:[131,2,1,""],set_power_status_async:[131,2,1,""]},"bosdyn.client.spot_cam.ptz":{PtzClient:[132,1,1,""],shift_pan_angle:[132,5,1,""]},"bosdyn.client.spot_cam.ptz.PtzClient":{default_service_name:[132,3,1,""],get_ptz_position:[132,2,1,""],get_ptz_position_async:[132,2,1,""],get_ptz_velocity:[132,2,1,""],get_ptz_velocity_async:[132,2,1,""],initialize_lens:[132,2,1,""],initialize_lens_async:[132,2,1,""],list_ptz:[132,2,1,""],list_ptz_async:[132,2,1,""],service_type:[132,3,1,""],set_ptz_position:[132,2,1,""],set_ptz_position_async:[132,2,1,""],set_ptz_velocity:[132,2,1,""],set_ptz_velocity_async:[132,2,1,""]},"bosdyn.client.spot_cam.streamquality":{StreamQualityClient:[133,1,1,""]},"bosdyn.client.spot_cam.streamquality.StreamQualityClient":{default_service_name:[133,3,1,""],enable_congestion_control:[133,2,1,""],enable_congestion_control_async:[133,2,1,""],get_stream_params:[133,2,1,""],get_stream_params_async:[133,2,1,""],service_type:[133,3,1,""],set_stream_params:[133,2,1,""],set_stream_params_async:[133,2,1,""]},"bosdyn.client.spot_cam.version":{VersionClient:[134,1,1,""]},"bosdyn.client.spot_cam.version.VersionClient":{default_service_name:[134,3,1,""],get_software_version:[134,2,1,""],get_software_version_async:[134,2,1,""],get_software_version_full:[134,2,1,""],get_software_version_full_async:[134,2,1,""],service_type:[134,3,1,""]},"bosdyn.client.spot_check":{CameraCalibrationCalibrationError:[135,4,1,""],CameraCalibrationInternalError:[135,4,1,""],CameraCalibrationPowerError:[135,4,1,""],CameraCalibrationResponseError:[135,4,1,""],CameraCalibrationRobotCommandError:[135,4,1,""],CameraCalibrationTargetNotCenteredError:[135,4,1,""],CameraCalibrationTimedOutError:[135,4,1,""],CameraCalibrationUserCanceledError:[135,4,1,""],SpotCheckCameraTimeoutError:[135,4,1,""],SpotCheckClient:[135,1,1,""],SpotCheckEndstopTimeoutError:[135,4,1,""],SpotCheckError:[135,4,1,""],SpotCheckGroundCheckError:[135,4,1,""],SpotCheckImuCheckError:[135,4,1,""],SpotCheckLoadcellTimeoutError:[135,4,1,""],SpotCheckNotSittingError:[135,4,1,""],SpotCheckPowerOnFailure:[135,4,1,""],SpotCheckResponseError:[135,4,1,""],SpotCheckStandFailureError:[135,4,1,""],SpotCheckTimedOutError:[135,4,1,""],SpotCheckUnexpectedPowerChangeError:[135,4,1,""],run_camera_calibration:[135,5,1,""],run_spot_check:[135,5,1,""]},"bosdyn.client.spot_check.SpotCheckClient":{camera_calibration_command:[135,2,1,""],camera_calibration_command_async:[135,2,1,""],camera_calibration_feedback:[135,2,1,""],camera_calibration_feedback_async:[135,2,1,""],default_service_name:[135,3,1,""],service_type:[135,3,1,""],spot_check_command:[135,2,1,""],spot_check_command_async:[135,2,1,""],spot_check_feedback:[135,2,1,""],spot_check_feedback_async:[135,2,1,""]},"bosdyn.client.time_sync":{InactiveThreadError:[136,4,1,""],NotEstablishedError:[136,4,1,""],TimeSyncClient:[136,1,1,""],TimeSyncEndpoint:[136,1,1,""],TimeSyncError:[136,4,1,""],TimeSyncThread:[136,1,1,""],TimedOutError:[136,4,1,""],robot_time_range_from_datetimes:[136,5,1,""],robot_time_range_from_nanoseconds:[136,5,1,""],timespec_to_robot_timespan:[136,5,1,""]},"bosdyn.client.time_sync.TimeSyncClient":{default_service_name:[136,3,1,""],get_time_sync_update:[136,2,1,""],get_time_sync_update_async:[136,2,1,""],service_type:[136,3,1,""]},"bosdyn.client.time_sync.TimeSyncEndpoint":{clock_identifier:[136,2,1,""],clock_skew:[136,2,1,""],establish_timesync:[136,2,1,""],get_new_estimate:[136,2,1,""],get_robot_time_converter:[136,2,1,""],has_established_time_sync:[136,2,1,""],response:[136,2,1,""],robot_timestamp_from_local_secs:[136,2,1,""],round_trip_time:[136,2,1,""]},"bosdyn.client.time_sync.TimeSyncThread":{DEFAULT_TIME_SYNC_INTERVAL_SEC:[136,3,1,""],TIME_SYNC_SERVICE_NOT_READY_INTERVAL_SEC:[136,3,1,""],endpoint:[136,2,1,""],get_robot_clock_skew:[136,2,1,""],get_robot_time_converter:[136,2,1,""],has_established_time_sync:[136,2,1,""],robot_timestamp_from_local_secs:[136,2,1,""],should_exit:[136,2,1,""],start:[136,2,1,""],stop:[136,2,1,""],stopped:[136,2,1,""],thread_exception:[136,2,1,""],time_sync_interval_sec:[136,2,1,""],wait_for_sync:[136,2,1,""]},"bosdyn.client.token_cache":{ClearFailedError:[137,4,1,""],NotInCacheError:[137,4,1,""],TokenCache:[137,1,1,""],TokenCacheError:[137,4,1,""],TokenCacheFilesystem:[137,1,1,""],WriteFailedError:[137,4,1,""],atomic_file_write:[137,5,1,""]},"bosdyn.client.token_cache.TokenCache":{clear:[137,2,1,""],match:[137,2,1,""],read:[137,2,1,""],write:[137,2,1,""]},"bosdyn.client.token_cache.TokenCacheFilesystem":{clear:[137,2,1,""],match:[137,2,1,""],read:[137,2,1,""],write:[137,2,1,""]},"bosdyn.client.token_manager":{TokenManager:[138,1,1,""]},"bosdyn.client.token_manager.TokenManager":{is_alive:[138,2,1,""],stop:[138,2,1,""],update:[138,2,1,""]},"bosdyn.client.util":{DedupLoggingMessages:[139,1,1,""],GrpcServiceRunner:[139,1,1,""],add_base_arguments:[139,5,1,""],add_common_arguments:[139,5,1,""],add_payload_credentials_arguments:[139,5,1,""],add_service_endpoint_arguments:[139,5,1,""],add_service_hosting_arguments:[139,5,1,""],cli_auth:[139,5,1,""],cli_login_prompt:[139,5,1,""],default_app_token_path:[139,5,1,""],does_dedup_filter_exist:[139,5,1,""],get_logger:[139,5,1,""],setup_logging:[139,5,1,""]},"bosdyn.client.util.DedupLoggingMessages":{filter:[139,2,1,""]},"bosdyn.client.util.GrpcServiceRunner":{run_until_interrupt:[139,2,1,""],stop:[139,2,1,""]},"bosdyn.client.world_object":{WorldObjectClient:[140,1,1,""],make_add_world_object_req:[140,5,1,""],make_change_world_object_req:[140,5,1,""],make_delete_world_object_req:[140,5,1,""]},"bosdyn.client.world_object.WorldObjectClient":{default_service_name:[140,3,1,""],list_world_objects:[140,2,1,""],list_world_objects_async:[140,2,1,""],mutate_world_objects:[140,2,1,""],mutate_world_objects_async:[140,2,1,""],service_type:[140,3,1,""],timesync_endpoint:[140,2,1,""],update_from:[140,2,1,""]},"bosdyn.geometry":{EulerZXY:[161,1,1,""],to_euler_zxy:[161,5,1,""]},"bosdyn.geometry.EulerZXY":{to_quaternion:[161,2,1,""]},"bosdyn.mission":{client:[164,0,0,"-"],constants:[165,0,0,"-"],exceptions:[166,0,0,"-"],remote_client:[167,0,0,"-"],server_util:[168,0,0,"-"],util:[169,0,0,"-"]},"bosdyn.mission.client":{CompilationError:[164,4,1,""],InvalidAnswerCode:[164,4,1,""],InvalidQuestionId:[164,4,1,""],MissionClient:[164,1,1,""],MissionResponseError:[164,4,1,""],NoMissionError:[164,4,1,""],NoMissionPlayingError:[164,4,1,""],QuestionAlreadyAnswered:[164,4,1,""],ValidationError:[164,4,1,""]},"bosdyn.mission.client.MissionClient":{answer_question:[164,2,1,""],answer_question_async:[164,2,1,""],default_service_name:[164,3,1,""],get_info:[164,2,1,""],get_info_async:[164,2,1,""],get_mission:[164,2,1,""],get_mission_async:[164,2,1,""],get_state:[164,2,1,""],get_state_async:[164,2,1,""],load_mission:[164,2,1,""],load_mission_as_chunks:[164,2,1,""],load_mission_async:[164,2,1,""],pause_mission:[164,2,1,""],pause_mission_async:[164,2,1,""],play_mission:[164,2,1,""],play_mission_async:[164,2,1,""],restart_mission:[164,2,1,""],restart_mission_async:[164,2,1,""],service_type:[164,3,1,""],stop_mission:[164,2,1,""],stop_mission_async:[164,2,1,""],timesync_endpoint:[164,2,1,""],update_from:[164,2,1,""]},"bosdyn.mission.constants":{Result:[165,1,1,""]},"bosdyn.mission.constants.Result":{ERROR:[165,3,1,""],FAILURE:[165,3,1,""],RUNNING:[165,3,1,""],SUCCESS:[165,3,1,""]},"bosdyn.mission.exceptions":{CompileError:[166,4,1,""],Error:[166,4,1,""],InaccessibleParameterError:[166,4,1,""],MessageOverrideError:[166,4,1,""],MissingParameterError:[166,4,1,""],NodeUnreferenceableError:[166,4,1,""],UnknownType:[166,4,1,""],ValidationError:[166,4,1,""]},"bosdyn.mission.exceptions.CompileError":{get_node_details:[166,2,1,""],node_impl:[166,2,1,""],node_name:[166,2,1,""]},"bosdyn.mission.remote_client":{Error:[167,4,1,""],InvalidSessionId:[167,4,1,""],MissingInputs:[167,4,1,""],MissingLeases:[167,4,1,""],RemoteClient:[167,1,1,""],tree_status_from_tick_status:[167,5,1,""]},"bosdyn.mission.remote_client.RemoteClient":{default_service_name:[167,3,1,""],establish_session:[167,2,1,""],establish_session_async:[167,2,1,""],service_type:[167,3,1,""],stop:[167,2,1,""],stop_async:[167,2,1,""],teardown_session:[167,2,1,""],teardown_session_async:[167,2,1,""],tick:[167,2,1,""],tick_async:[167,2,1,""]},"bosdyn.mission.server_util":{ResponseContext:[168,1,1,""]},"bosdyn.mission.util":{Error:[169,4,1,""],InvalidConversion:[169,4,1,""],ResultFromProto:[169,1,1,""],field_desc_to_pb_type:[169,5,1,""],get_value_from_constant_value_message:[169,5,1,""],get_value_from_value_message:[169,5,1,""],is_string_identifier:[169,5,1,""],most_restrictive_travel_params:[169,5,1,""],node_spec_to_short_string:[169,5,1,""],one_line_str:[169,5,1,""],proto_enum_to_result_constant:[169,5,1,""],proto_from_tuple:[169,5,1,""],python_type_to_pb_type:[169,5,1,""],python_var_to_value:[169,5,1,""],result_constant_to_proto_enum:[169,5,1,""],safe_pb_enum_to_string:[169,5,1,""],safe_pb_type_to_string:[169,5,1,""],tree_to_string:[169,5,1,""]},"bosdyn.mission.util.ResultFromProto":{proto_from_results:[169,3,1,""],results_from_proto:[169,3,1,""]},"bosdyn.util":{DatetimeParseError:[162,4,1,""],RobotTimeConverter:[162,1,1,""],distance_str:[162,5,1,""],duration_str:[162,5,1,""],duration_to_seconds:[162,5,1,""],format_metric:[162,5,1,""],now_nsec:[162,5,1,""],now_sec:[162,5,1,""],now_timestamp:[162,5,1,""],nsec_to_sec:[162,5,1,""],nsec_to_timestamp:[162,5,1,""],parse_datetime:[162,5,1,""],parse_timespan:[162,5,1,""],sec_to_nsec:[162,5,1,""],seconds_to_duration:[162,5,1,""],seconds_to_timestamp:[162,5,1,""],secs_to_hms:[162,5,1,""],set_timestamp_from_datetime:[162,5,1,""],set_timestamp_from_now:[162,5,1,""],set_timestamp_from_nsec:[162,5,1,""],timestamp_str:[162,5,1,""],timestamp_to_datetime:[162,5,1,""],timestamp_to_nsec:[162,5,1,""],timestamp_to_sec:[162,5,1,""]},"bosdyn.util.RobotTimeConverter":{convert_timestamp_from_local_to_robot:[162,2,1,""],robot_seconds_from_local_seconds:[162,2,1,""],robot_timestamp_from_local:[162,2,1,""],robot_timestamp_from_local_nsecs:[162,2,1,""],robot_timestamp_from_local_secs:[162,2,1,""]},bosdyn:{geometry:[161,0,0,"-"],util:[162,0,0,"-"]}},objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","method","Python method"],"3":["py","attribute","Python attribute"],"4":["py","exception","Python exception"],"5":["py","function","Python function"]},objtypes:{"0":"py:module","1":"py:class","2":"py:method","3":"py:attribute","4":"py:exception","5":"py:function"},terms:{"000":[17,61],"0001":66,"00044be03a91":230,"0012627621181309223":35,"002":66,"003905495163053274":4,"005":66,"00675":48,"009":225,"010":66,"0126167":48,"014":66,"0166667":48,"01t00":190,"020":66,"022":66,"028":61,"045":48,"04t00":190,"095":48,"0x00":25,"0x01":25,"0x02":25,"0xff":25,"100":[17,20,31,58,61,62,67,69,82,101,208],"1000":69,"1000000":164,"1000base":[4,53],"1048576":125,"1056":66,"1059951782226562":4,"109":225,"10cm":[69,215],"10m":187,"10ma":53,"110":227,"1100":4,"1146":221,"1196":188,"11ea":230,"120":4,"1200":135,"1234":189,"127":[56,69],"130":8,"13a":53,"13m":48,"146mm":20,"14875":62,"1500":[69,230],"150uf":53,"150w":[4,53],"15100":56,"1581869515":[187,192],"1583941992":66,"1583953617":66,"1585258226":66,"1585258227":66,"1585323526":66,"1585324337":66,"160":[4,25,69,143],"1601524800":37,"1601611200":37,"168":[48,49,55,56,60,62,63,64,65,66,122,189,224,225,227,230],"16th":[29,31],"180":[4,32,50,69,135,208],"1800":8,"189":65,"190mm":52,"191":4,"192":[48,49,55,56,60,62,63,64,65,66,122,189,224,225,227,230],"19557":[6,69],"19904":[65,66],"1e6":67,"1hz":53,"1km":17,"1min":69,"200":[61,66],"20000":[48,61],"20022":[48,49,56],"20080":48,"20180414":[65,66],"2019":[65,66],"20190601":69,"2020":[35,37,52,59,65,66,187,190,192,225],"20200120":[187,192],"20200120_120000":[187,192],"20201030":192,"20201031":192,"20201031_115000":192,"20201031_115950":192,"20201107":[187,192],"20201108":[187,192],"2021":[62,65,69,225],"2022":187,"20443":48,"2048":156,"20896":96,"20c":4,"20m":187,"21000":[48,56],"21174180507659912":4,"21443":[55,56],"2148320923085":69,"2158320923085":69,"217":69,"21900":[49,224],"21mm":51,"22000":48,"224952738":66,"224990830":66,"246":66,"250":31,"255":[2,27,32,48,62,63,69,227,230],"256":[187,192],"259383":[65,66],"25mm":54,"26704":62,"275":66,"276":66,"280242100":66,"283":65,"28906":66,"290":66,"29t18":35,"29t183020z":35,"29t185610z":35,"29t185610z_inspect":35,"2cm":69,"2step":32,"2x1":110,"300":[4,61,62],"30000":48,"30022":48,"30080":48,"300uf":53,"30443":48,"30s":67,"30x":69,"31000":48,"312":60,"319":213,"32000":48,"32049":66,"323":61,"3339":190,"338":225,"339":225,"340":[61,225],"352":61,"357":60,"35v":53,"360":[4,33,46,67,69,132,245],"3600":37,"363":66,"364":66,"36h11":69,"36m":205,"384":[61,137],"3mm":[51,54],"3rd":225,"3rdparti":17,"3x1":110,"3x3":110,"400":[31,60],"400w":4,"401":69,"403":37,"427":60,"429":67,"441":60,"443":[48,66],"450":31,"45c":4,"480":60,"491d":48,"4mb":[44,69],"4mm":54,"4x4":[41,110,148],"500":[4,63],"50051":[62,64,221],"50w":8,"54d4":230,"5708":69,"571":66,"577":225,"585":225,"5900":56,"5901":56,"59vabsolut":53,"5cm":69,"5gb":17,"5mm":51,"5ppm":53,"5ppmpp":53,"5x5":60,"6006":61,"600w":53,"602":225,"605":4,"60cm":20,"610":66,"615":66,"616":66,"616570624":66,"63mm":51,"640":60,"640x640":61,"644209920":66,"650":66,"65535":69,"695":225,"6x1":110,"6x6":[69,110],"707":64,"7071":6,"71828":69,"729":66,"72v":53,"72vmax":53,"738928c4410c":48,"786897121z":35,"78b076a2":48,"802":4,"80v":53,"8212":64,"826":66,"840":4,"854":61,"86274254322052":4,"8bit":[2,69],"8ien":214,"8mm":54,"90490007":[65,66],"9365":230,"9815ea67e2122dfd3eb2003716add29987e7daa1":61,"984":8,"9903":[65,66],"996009984":66,"998":242,"abstract":[0,44,69,83,102],"boolean":[2,29,31,45,46,69,73,74,86,88,100,102,104,117,119,126,136,139,205],"break":[2,36,42,60,61,62,63,64,66,69],"byte":[2,25,46,67,69,88,89,90,123,139,147,151,158,160],"case":[2,9,18,19,20,24,36,39,40,42,44,45,52,58,61,63,65,66,67,69,74,84,93,99,101,113,118,119,213,220,224,227,228,236],"catch":[24,67,88],"class":[25,38,40,44,46,59,60,61,62,65,66,67,69,73,74,76,77,78,79,82,83,84,85,87,88,89,90,91,92,93,94,95,96,98,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,164,165,167,168,169,190,231,233],"const":[22,32,69],"default":[2,6,14,17,27,28,29,31,32,37,44,46,48,49,55,56,58,60,62,63,65,66,67,73,74,82,84,88,90,93,100,101,102,104,107,113,114,117,118,119,122,123,137,139,148,159,162,172,187,188,190,205,208,213,221,224,225,226,227,228,230,231,233,236,238,240,242],"enum":[2,13,20,29,31,37,44,58,67,69,88,96,104,117,165,169],"export":[62,63,64,188,231],"final":[2,3,14,16,22,25,42,60,61,66,67,69,100,213,214,242],"float":[2,25,46,63,69,74,86,88,90,102,110,115,118,119,126,135,136,140,148,162],"function":[19,28,29,31,33,36,38,41,43,44,47,53,60,62,63,64,69,73,74,84,88,101,102,104,109,114,115,118,119,121,123,135,139,148,162,173,186,188,190,204,220,227,228,234,236,241,245],"goto":[22,69],"import":[10,21,24,29,37,39,40,46,49,55,60,61,62,63,65,66,67,69,73,90,118,189,224],"int":[2,25,46,62,63,69,74,82,85,86,87,88,90,100,101,102,122,123,136,139,147,148,149,151,154,155,157,158,162,164],"long":[2,3,15,17,20,21,24,31,32,39,40,46,61,62,63,64,66,67,69,88,177,213],"new":[2,5,6,14,15,16,17,19,21,22,27,29,32,36,37,38,39,40,41,42,44,45,48,49,55,56,58,60,61,62,63,64,66,69,73,74,78,88,93,96,98,100,104,109,113,118,139,148,149,190,213,223,225,227,228,236],"null":56,"public":[1,68,69,97],"return":[2,4,6,10,15,16,17,21,22,24,29,31,32,33,35,37,42,43,44,45,46,53,55,58,60,62,63,64,65,66,68,69,73,74,75,76,78,81,82,84,85,86,87,88,89,92,93,94,95,96,98,99,100,101,102,104,106,108,109,110,111,112,113,114,115,117,118,119,120,121,122,123,129,134,135,136,137,139,140,143,144,147,148,149,151,152,154,155,156,157,158,159,160,162,166,169,192,203,213,220,221,225,227,228,236,241],"short":[31,32,39,44,45,46,53,66,67,69,192,214],"static":[22,24,41,44,69,74,84,100,102,104,107,110,117,119,122,149,164,227],"super":[62,69,104],"switch":[19,21,29,67,69,173,220],"throw":[2,63,64,66,67,102,135,169,227],"transient":[65,66,67,97],"true":[2,14,22,27,31,32,46,49,60,61,62,63,64,66,69,73,74,81,83,86,88,93,96,99,100,102,104,109,115,117,118,119,121,123,126,129,133,136,139,144,160,162,169,205,224],"try":[2,6,8,10,17,19,21,24,27,49,61,63,64,65,67,69,84,88,90,97,100,109,117,118,119,121,122,131,187,190,195,225,227,242],"var":[22,69,169],"while":[2,6,7,8,10,12,15,17,19,21,22,24,25,28,29,31,32,33,35,40,43,44,45,46,50,60,61,62,63,64,65,67,69,88,96,100,115,117,119,135,172,190,195,196,205,213,214,219,223,236,240],AND:69,AWS:[67,224],Added:67,Adding:192,And:[31,55,65,69,188,196,213],Are:[65,93,113],BPS:69,But:[65,213],DNS:[44,69],Doing:27,For:[2,3,4,6,7,9,10,13,14,16,17,19,20,21,22,24,25,27,28,29,31,35,36,37,41,42,43,44,45,46,49,50,51,55,58,60,62,63,64,65,66,67,69,73,74,78,88,90,95,96,98,100,101,104,108,109,110,111,115,117,119,120,121,125,126,127,128,129,130,131,132,133,134,137,138,140,164,171,172,174,175,176,177,178,179,180,181,182,183,184,185,187,188,189,190,192,194,205,208,213,214,215,217,221,224,225,227,228,231,233,236,242,245],GPS:[17,19,35,36,46,190,216],Has:[25,67],ICE:[69,130,230],IDE:65,IDs:[15,18,40,69,88,214],LTS:[59,65,67],MKS:58,NOT:[6,21,39,69],Near:29,Not:[2,31,44,53,56,61,63,66,69],OLED:227,One:[2,17,19,29,35,39,65,67,69,79,100,104,109,117,213,232],PPS:8,THE:190,That:[17,22,60,67,69,171,172,174,175,176,177,178,179,180,181,182,183,184,185,194,213,217,231,233,245],The:[0,2,3,4,6,7,8,9,10,11,12,13,15,16,17,18,19,20,21,22,24,25,26,27,28,29,30,31,32,33,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,69,70,73,74,75,76,77,78,79,82,84,85,86,87,88,90,92,93,94,95,96,97,98,99,100,101,102,104,107,108,109,110,111,113,114,115,117,118,119,121,123,135,136,138,139,140,146,147,159,162,163,164,166,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,187,188,189,190,191,192,196,197,198,199,200,201,202,203,204,205,208,213,214,215,216,217,220,221,222,223,224,225,226,227,228,230,231,232,233,236,238,240,242,244,245],Their:58,Then:[2,7,24,29,32,49,60,65,69,174,217,228,236],There:[2,6,15,17,19,24,25,27,28,29,31,35,38,39,40,42,44,45,46,55,60,61,65,66,67,69,100,104,118,119,164,175,178,184,187,190,205,214,221,225,238,242],These:[0,1,2,6,17,24,25,27,28,31,36,37,38,40,41,42,44,45,46,48,49,55,61,66,67,68,69,74,110,119,173,186,187,190,192,193,195,205,212,213,214,215,221,223,227,228,230,231,233,234,243,244],UIs:69,USING:231,Use:[17,29,40,48,55,56,58,60,61,62,63,65,67,69,80,88,100,104,119,173,190,195,214,219,223,225,227,234,242],Used:[7,67,69],Useful:[69,96],Uses:[2,18,67,69,82],Using:[17,20,24,41,58,66,67,69,99,101,104,113,172,174,175,176,177,179,180,181,182,183,185,194,205,207,211,213,214,215,216,220,229,236,240,241,242],WAS:69,WILL:190,Will:[32,69,96,100,117,195],With:[6,24,32,43,64,65,67,73,190,202],__call__:66,__init__:[62,227],__main__:[60,62,63,65,66],__name__:[60,62,63],_build_auth_request:44,_channel:66,_end_unary_response_block:66,_endpoint:96,_error_from_respons:44,_handler:107,_inactiverpcerror:[65,66],_label_:[62,63],_leas:213,_lease_cli:213,_lease_keepal:213,_lease_wallet:213,_max_x_spe:205,_max_y_spe:205,_movement_on:205,_periodic_check_in:104,_setaccesspoint:227,_standup:205,_stub:44,_token_from_respons:44,_unique_id:96,a099:48,a_adjoint_b:110,a_adjoint_b_matrix:110,a_tform_b:110,a_tform_c:110,aaron:67,abc:102,abil:[6,18,33,45,48,52,67,69],abl:[6,17,21,22,29,37,40,43,44,45,46,48,62,66,67,69,74,88,208,214,228,242],abort:[67,69,100],about:[2,3,7,11,12,15,16,17,24,25,27,29,31,32,37,39,40,41,42,45,46,47,48,51,55,60,62,63,65,66,67,69,85,87,92,93,104,122,136,164,174,177,192,193,197,198,209,213,231,236],abov:[2,4,17,20,22,24,29,33,36,41,44,49,50,52,55,56,60,61,62,63,64,65,66,67,69,139,188,189,208,213,217,219,221,223,227,228,231,240],abruptli:45,absent:[2,27],absolut:[2,31,32,67,164,231,238],absolute_mot:[2,32],absolute_path_to_pb:231,absolute_posit:[2,32],absolute_transl:[2,32],absolute_yaw:[2,32],absorb:50,acceler:[4,6,69],accept:[31,42,44,48,54,61,62,63,67,69,84,119,166,221,228,231,233],accepted_answer_cod:69,access:[9,12,17,18,21,22,24,25,28,29,33,37,40,44,45,47,48,49,55,56,60,62,66,67,69,82,88,92,93,96,97,102,104,106,110,113,118,119,120,123,139,143,148,149,154,208,213,224,227,231,242],accessor:[74,118,119,140,152,164],accessori:8,accident:[42,46,67],accommod:53,accompani:104,accomplish:[6,20,67,69,77,170,211],accord:[32,67,69,88],accordingli:50,account:[24,44,55,67,188],accross:69,accumul:19,accur:[41,46,67,69,117,182],accuraci:[7,53,61,69,119,213],achiev:[2,17,19,24,31,55,67,69,118,119,136,205,242],acquaint:170,acquir:[24,36,37,42,45,46,66,67,69,78,83,85,86,87,93,102,104,105,113,119,186,213,220,231,236,241,245],acquire_and_process_request:86,acquire_async:104,acquire_data:[35,85],acquire_data_async:85,acquire_data_error:85,acquire_data_from_request:[67,85],acquire_data_from_request_async:85,acquire_data_request_nam:69,acquire_plugin_data:87,acquire_plugin_data_async:87,acquire_response_fn:[46,88],acquiredata:[36,69,85,86,87,88,236],acquiredatarequest:[35,36,85],acquiredatarespons:85,acquireleas:[42,69],acquireplugindata:[36,46,69,88],acquireplugindatarequest:[46,88],acquireplugindatarespons:[46,88],acquisit:[34,38,41,45,49,68,69,74,75,83,170,227],acquisition_request:[46,69,85,86,87],acquisition_tim:[41,69],acquisition_time_second:46,acquisition_timestamp:69,acquisitionrequestlist:[85,86,87],acquist:69,across:[17,19,24,44,45,46,49,50,62,69,74,113,227,228],act:[10,29,44,45,66,69,118],action:[9,17,19,23,29,35,38,46,56,58,62,63,67,77,85,86,87,89,104,140,225],action_add:69,action_chang:69,action_delet:69,action_id:[35,46,69,87,89],action_nam:[35,36,69,85,86],action_unknown:69,activ:[8,10,17,18,22,29,32,33,40,42,44,45,46,60,61,62,63,65,66,67,69,83,98,100,104,135,231,236],active_config:69,active_leas:104,actual:[27,30,31,65,69,74,99,107,213,225,240],actuat:[4,39,45,53,67,69],acycl:69,adapt:[18,55,235],add:[0,6,22,27,29,33,37,41,45,49,56,60,62,63,64,65,67,69,77,83,84,90,104,107,109,110,118,123,139,140,148,149,156,188,213,214,221,224,228,231,233,242,243],add_app_token:82,add_argu:[60,62,63],add_base_argu:139,add_blob:[67,90,191],add_blob_async:90,add_common_argu:[60,62,63,139],add_done_callback:84,add_edge_to_tre:[41,99],add_error:[46,88],add_ev:90,add_events_async:90,add_image_coordin:67,add_imageserviceservicer_to_serv:38,add_insecure_port:62,add_lease_wallet_processor:104,add_log_blob:107,add_log_blob_async:107,add_log_protobuf:107,add_log_protobuf_async:107,add_message_seri:148,add_networkcomputebridgeworkerservicer_to_serv:62,add_operator_com:[90,107],add_operator_comment_async:[90,107],add_payload_credentials_argu:139,add_pod_seri:148,add_proto_read:152,add_protobuf:[67,90],add_protobuf_async:90,add_request:88,add_sav:88,add_seri:[148,149],add_series_descriptor:149,add_service_endpoint_argu:139,add_service_hosting_argu:139,add_servicer_to_server_fn:[38,123,139],add_signal_tick:90,add_signal_tick_async:90,add_task:77,add_text_messag:[90,107],add_text_messages_async:[90,107],added:[5,9,14,21,27,28,29,32,33,35,41,45,50,54,67,69,74,88,118,119,123,140,148,149,187,192,205,214,220,228],adding:[29,43,67,69,90,91,107,140,208],addit:[0,2,17,19,21,25,26,27,31,37,38,41,45,46,49,56,58,64,66,69,73,82,86,88,122,123,148,149,156,159,170,187,225,227,228,229,236,242],addition:[17,25,26,28,29,30,31,32,33,42,46,67,69,84,118,205,208,214,227,229],additional_index:[69,148,149,159],additional_index_nam:[69,148,149,159],additional_param:86,additional_properti:[62,63,69],addlogannot:69,addlogannotationrequest:123,addlogannotationrespons:67,addrequesthead:116,address:[25,30,37,40,44,48,55,56,60,62,64,66,67,69,81,82,83,84,93,122,130,187,190,221,222,225,227,228,230,242],addserieserror:146,aded:69,adequ:[53,228],adher:17,adjac:[19,52,69],adjoint:110,adjust:[2,18,22,24,27,28,29,31,32,33,55,67,69,205,224,242],adjust_paramet:69,admin:[24,48,55,56,65,69,228],administr:[56,65,67,69],admiss:2,admitt:69,admittance_setting_loos:69,admittance_setting_norm:69,admittance_setting_off:69,admittance_setting_stiff:69,admittance_setting_unknown:69,admittance_setting_very_stiff:69,adopt:[41,74,84,85,87,89,90,91,94,100,107,115,140,164,190],advanc:[26,30,44,55,61,69,104,119],advantag:[6,40,63,64,93,229,231],advers:67,adversari:44,advertis:[111,236],advis:69,aerial:69,affect:[2,17,29,40,45,55,67,69,231],afford:69,after:[2,6,7,14,16,17,19,20,24,27,28,29,30,31,32,33,37,40,44,45,48,49,55,56,58,60,61,62,63,64,65,66,67,69,84,88,93,96,100,110,113,119,136,140,174,190,208,212,213,221,224,228,236,242,245],afterward:[17,67],again:[29,42,55,61,63,64,65,66,67,69,74,100,109,117,164,225,232],against:[7,20,22,29,39,61,69,104,213],aggreg:[36,69,190],aggress:69,agil:52,ago:[62,66,187,192],agre:58,ahead:2,aid:[13,19,28,42,67,84],aim:22,air:[2,69],aka:[67,69],algebra:64,algorithm:[20,61,69,135,227],align:[2,6,7,17,41,43,45,63,64,66,69,99,110,117,119,174,213,221],aliv:[38,40,42,45,62,67,69,93,104,107,113,135,190,242],all:[0,2,6,14,17,22,23,24,25,28,29,31,32,33,35,36,37,38,39,40,41,43,44,45,46,47,48,49,50,52,53,55,56,58,59,61,62,63,64,65,66,67,69,72,83,84,85,86,87,88,92,93,97,98,99,101,102,104,107,111,112,114,116,118,119,121,122,130,132,134,139,140,149,160,164,171,172,173,187,190,192,199,204,205,208,213,214,217,220,223,224,225,227,228,230,231,232,233,234,236,242],alloc:37,allot:97,allow:[2,3,5,6,7,10,12,14,16,18,19,21,22,25,28,29,31,32,36,37,39,42,43,45,46,48,49,53,55,56,64,66,67,69,74,82,90,92,93,96,97,102,105,110,112,113,114,117,119,123,139,178,184,189,190,214,220,221,224,225,226,227,229,231,232,233,236,242,245],allow_async:96,allow_degraded_percept:69,allow_super_leas:104,allowable_orient:[63,69],almost:[58,61,97],alon:69,along:[2,7,9,10,14,17,18,19,23,25,37,54,58,61,62,64,67,69,100,119,171,173,205,220,227],alongsid:[10,29,67,69,171],alpha:[2,69],alreadi:[2,7,15,22,27,31,40,43,49,61,62,63,64,66,67,69,74,88,93,98,100,104,107,113,117,118,119,125,132,136,139,144,148,149,164,228,242],also:[0,2,5,6,8,10,17,18,19,21,22,24,27,28,29,31,32,36,37,40,41,44,45,46,47,49,50,53,55,56,58,60,64,65,66,67,69,93,100,104,118,119,122,171,187,189,204,205,213,214,216,221,223,225,227,228,231,233,239,242,245],alter:[2,31],altern:[14,16,42,67,69,85,189,224,226,242],alternate_robot_tform_waypoint:69,although:[24,37,44],aluminum:54,alwai:[2,8,17,22,29,31,32,33,40,41,46,58,62,63,64,65,67,69,139,169,189],always_print_logger_level:139,always_reprompt:69,always_restart:[22,69,169],amazon:[67,188],ambigu:[67,69,115],ambl:[32,67,69,245],among:[69,110],amount:[2,10,18,21,27,28,32,36,37,44,45,61,63,67,69,102,182,213,231,233],amp:[69,220,241,245],amplitud:[2,32],anaconda:65,analysi:[23,37,41,69],anchor:[9,67,100,109,197,215],anchoring_on_server_was_modifi:69,anchoring_respons:213,anchoringhint:[109,213],ang_i:110,ang_interp_cubic_eul:69,ang_interp_linear:69,ang_interp_unknown:69,ang_interpol:69,ang_x:110,ang_z:110,angl:[2,6,27,29,32,64,67,69,100,110,132,161],angle_diff:110,angle_diff_degre:110,angular:[6,35,58,64,67,69,110],angular_velocity_of_hand_rt_odom_in_hand:69,ani:[2,3,5,6,8,9,10,13,15,17,19,20,21,22,23,25,27,29,30,31,32,33,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,54,56,60,62,63,65,66,67,69,73,74,84,85,87,88,90,93,96,98,100,102,104,107,111,113,117,118,119,123,135,136,139,162,166,171,190,205,208,213,214,220,225,228,231,233,236,242,244,245],anim:[26,29,32,67,71,74,203],animate_move_params_fil:73,animate_param:2,animated_fil:73,animated_mov:2,animated_move_generated_id:2,animatedmov:2,animateparam:27,animation_file_conversion_help:72,animation_file_to_proto:[27,28,31,72,73,74],animation_fram:72,animation_keyfram:2,animation_move_prefix:74,animation_nam:2,animation_proto:74,animation_record:171,animation_tform_bodi:2,animationkeyfram:31,animationuploadhelp:74,animationvalidationfailederror:74,annon:67,annot:[13,17,18,21,31,48,60,61,62,63,64,67,68,75,117,143,144,148,149,156,159,218,244],annotation_state_non:69,annotation_state_set:69,annotation_state_unknown:69,announc:[44,48,69,242],announce_cli:228,announce_servic:228,anoth:[2,13,17,18,19,20,22,24,32,41,42,44,62,66,67,69,76,95,108,109,119,131,135,187,205,213,216,221,225,231,233],another_channel:35,answer:[22,67,69,130,164,197,213],answer_cod:69,answer_quest:164,answer_question_async:164,answered_quest:69,answerquest:[22,69],anymor:67,anyon:[10,29],anyth:[20,32,40,46,63,64,69,88,104],anywher:22,apart:[2,29,32,42,66],api:[0,1,2,6,7,9,11,12,13,16,18,19,20,21,22,23,24,25,26,28,30,34,37,40,41,43,44,46,53,60,61,62,63,64,65,66,68,69,71,74,75,76,78,79,81,85,86,87,88,89,90,91,92,93,94,95,96,97,98,100,101,102,103,104,105,106,107,109,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,140,141,142,143,162,164,167,169,170,172,173,174,175,176,177,179,180,181,182,183,185,187,190,191,192,193,194,195,196,198,201,205,208,210,211,213,214,221,223,226,227,229,234,236,238,242],api_grasp_overrid:69,api_vers:69,apiev:69,apigraspoverrid:67,apigraspoverriderequest:108,apigraspoverriderespons:108,app:[17,19,21,23,55,67,82,97,122,214,227],app_token:[44,78,82],appar:[69,213],appear:[24,27,28,29,31,40,45,46,48,55,60,66,67,69,100,213,225,227,242],append:[29,37,42,48,62,63,67,69,74,86,99,100,104,242,244],appl:221,appli:[2,6,7,17,27,31,32,33,45,55,67,69,74,93],applic:[0,2,3,12,13,18,19,23,24,26,27,29,30,31,33,40,41,42,43,44,45,46,47,48,55,56,57,59,62,66,67,69,78,82,87,96,104,118,139,188,190,202,205,213,218,224,225,227,231,232,242,244],application_token:[44,69],application_token_requir:93,approach:[19,24,29,33,44,58,69],appropri:[6,29,37,41,45,53,67,69,88,101,102,139,169],approxim:[13,17,60,63,69,208,213],april:[20,69,205,213,216],aprilrobot:205,apriltag:[17,20,41,45,67,69],apriltag_properti:69,apt:[56,189,231,239,245],arbitrari:[2,25,32,37,43,45,67,69,89,137,192,213,219,228],arbitrarili:[31,40,213],arc:177,architect:36,architectur:[24,46,66],area:[10,13,15,17,19,20,29,40,43,50,52,60,67,234],aren:[14,17,60,63,69,208],arg:[44,62,63,74,83,100,107,119,139,153,159,168],argpars:[60,62,63],arguement:69,argument:[29,30,33,35,44,46,49,56,60,62,63,64,65,66,67,69,73,74,78,83,84,88,90,91,96,97,104,107,112,113,114,119,126,139,173,178,188,189,190,192,205,206,208,210,213,214,221,223,224,225,227,229,230,231,233,234,236,238,240,242,245],argumentpars:[60,62,63],argv:[60,62,63],aris:[40,48,67],arm:[2,3,28,29,31,42,47,60,63,64,66,68,69,74,75,118,119,121,170,175,178,184],arm_and_mobility_command:172,arm_carry_command:[63,119],arm_cartesian_command:69,arm_cartesian_feedback:69,arm_command:[6,64,119],arm_command_feedback:69,arm_dance_frame_id:[2,27],arm_door:174,arm_drag_command:69,arm_drag_feedback:69,arm_entry_slic:[2,27],arm_gaz:176,arm_gaze_command:[69,119],arm_gaze_feedback:69,arm_grasp:[63,178],arm_joint:27,arm_joint_command:119,arm_joint_mov:179,arm_joint_move_command:69,arm_joint_move_feedback:69,arm_joints_handl:72,arm_move_frame_bodi:2,arm_move_frame_center_of_footprint:2,arm_move_frame_d:2,arm_move_frame_hand:2,arm_move_frame_shadow:2,arm_move_frame_should:2,arm_move_frame_unknown:2,arm_move_param:2,arm_playback:[2,27],arm_playback_default:2,arm_playback_jointspac:2,arm_playback_opt:72,arm_playback_workspac:2,arm_playback_workspace_dance_fram:2,arm_pose_command:[64,119],arm_prohibit:[2,27],arm_prohibited_opt:72,arm_ready_command:119,arm_requir:[2,27],arm_required_opt:72,arm_simpl:180,arm_stop_command:69,arm_stop_feedback:69,arm_stow_command:[63,64,119],arm_stow_unstow:181,arm_surface_contact:[76,182],arm_surface_contact_command:76,arm_surface_contact_command_async:76,arm_surface_contact_pb2:76,arm_surface_contact_servic:75,arm_trajectori:183,arm_velocity_command:69,arm_velocity_feedback:69,arm_walk_to_object:184,arm_with_body_follow:185,arm_wrench_command:119,armcartesiancommand:[6,7,119],armjointmovecommand:[6,67,119],armless:[69,119],armmov:2,armor:69,armsurfacecontact:[76,177],armsurfacecontactcli:[76,182],armsurfacecontactrequest:76,armsurfacecontactservic:[67,76],around:[2,6,8,10,14,17,20,21,28,29,32,35,41,44,45,51,52,60,62,64,66,67,69,78,119,120,138,172,178,184,185,214,216,233,234],arrai:[32,62,63,69,82,110,148,155,156,213],arrang:[13,29,69],arriv:[6,64,69],arrow:[29,62,69,216],arrow_length:69,arrow_radiu:69,ascend:21,ascii:[2,69],aservic:69,ask:[16,22,31,43,55,60,62,69,78,164,174],aspect:[19,69,170],assembl:67,assert:[69,96],assertionerror:107,assess:21,assign:[9,23,45,67,69,123,136,139,213],assist:[19,48,67,69,156],associ:[2,17,18,19,22,23,26,28,29,31,35,36,37,38,40,41,45,46,48,65,67,74,84,85,87,88,89,98,100,102,117,118,129,148,149,152,154,189,190,213,214,216,221,223,227,228,229,242,244],associated_metadata:89,associated_metadata_proto:46,associatedmetadata:[37,46,88,89],assum:[22,24,27,29,37,50,56,63,65,66,67,69,93,138,213,226,227,228],assume_zero_roll_and_pitch:2,assume_zero_roll_and_pitch_opt:72,assumpt:[7,69],assur:66,async:[46,67,74,75,76,79,84,85,87,88,89,90,91,94,95,96,98,100,101,103,104,106,107,108,111,114,115,117,119,121,125,126,127,128,129,130,131,132,133,134,135,136,140,164,198],async_task:77,asyncgrpctask:77,asynchron:[67,69,77,78,84,88,233],asyncperiodicgrpctask:77,asyncperiodicqueri:77,asynctask:77,ato:69,atomic_file_writ:137,attach:[6,17,19,25,38,40,44,47,48,49,52,67,69,90,104,113,118,121,123,139,228],attach_detach_payload:223,attach_payload:113,attach_payload_async:113,attack:[24,44,66,67],attempt:[6,8,14,19,21,24,29,31,32,33,39,42,44,46,50,55,62,67,69,74,94,96,100,102,113,115,118,119,136,195,227,228],attempt_counter_state_nam:69,attempted_leas:69,attent:[44,50],attribut:[2,69,110,169],attribute_nam:73,attribute_valu:73,audio:[31,67,68,124,230],audio_channel_external_m:69,audio_channel_internal_m:69,audio_channel_unknown:69,audio_pb2:125,audiocapturechannel:125,audiocli:125,audioservic:[67,125],augment:19,auth:[37,44,48,66,68,75,113,190,228],auth_admin_keep:56,auth_async:78,auth_servic:75,auth_with_token:78,auth_with_token_async:78,authclient:[44,78],authent:[24,44,45,46,48,49,60,62,63,67,69,78,86,88,97,118,139,191,192,213,227,228,239],authenticate_from_cach:118,authenticate_from_payload_credenti:118,authenticate_with_token:118,authmetadataplugin:82,author:[24,26,28,29,30,38,40,45,46,58,62,64,69,82,93,97,113,118,122,190,223,228,229,234,242],authorit:[69,104],authresponseerror:78,authservic:[24,44,66,67,78],auto:[6,7,28,31,32,38,40,60,68,69,75,126,203,208],auto_focu:230,auto_grasp_command:69,auto_push_command:69,auto_return:79,auto_return_pb2:79,auto_return_servic:75,auto_scal:[69,126],autodesk:[28,67],autofocu:[67,69,131,132],autogener:226,autograsp:7,autom:[22,67,69,138,178,228],automat:[2,6,7,10,15,22,27,28,29,31,33,36,40,42,44,46,48,55,56,61,62,63,67,69,74,79,113,123,136,174,184,191,208,214,215,227,228,229,236,245],autonom:[7,9,10,12,13,19,21,22,60,62,69,174,195],autonomi:[3,17,24,55,170],autonomous_robot_en:230,autopush:[7,67],autoreturn:[9,69,79,186],autoreturncli:79,autoreturnresponseerror:79,autoreturnservic:79,autowalk:[9,10,11,14,17,18,19,20,21,35,36,46,67,69,189,215,216,220,224,226],autowalk_miss:226,aux1:[69,131],aux2:[69,131],avail:[2,6,7,12,17,19,20,21,26,29,31,33,36,37,43,44,45,46,48,52,55,56,61,63,65,66,67,69,92,93,102,104,106,111,118,125,126,132,136,178,187,190,197,198,200,202,208,221,227,228,231,236],available_label:69,available_model:[62,69],averag:[2,53,61,69],avoid:[8,18,20,24,33,50,52,58,62,67,69,171,205,225],awai:[6,8,10,17,18,20,21,32,60,64,67,69,100,117,174,177,231],await:67,awar:[6,42,67,172,178,184,185],awb:69,awb_mod:133,aws:[188,224],aws_access_key_id:224,aws_bucket_nam:188,aws_secret_access_kei:224,axes:[2,32,41,69,213,216],axi:[2,4,6,32,41,63,66,67,69,110,213,216,245],axis_mode_forc:69,axis_mode_posit:69,axis_on_gripper_ewrt_gripp:[63,69],axis_to_align_with_ewrt_fram:[63,69],axis_to_align_with_ewrt_vis:63,azimuth:69,b11205d698e:[65,66],b4ba:48,b_tform_a:110,b_tform_c:110,back:[2,9,10,11,16,17,27,32,33,44,46,47,52,60,61,62,63,64,65,67,69,109,119,131,171,213,219,220,224,228,233,245],back_depth:66,back_depth_in_visual_fram:66,back_fisheye_imag:[60,63,66],backend:[37,97,213],background:[20,24,40,56,66,67,93,102,104,113,118,123,136,139,227],backlit:20,backspac:29,backward:[6,25,32,33,44,66,69,122,139],bad:[37,44,58,67,69,86,96,104,123],bad_pose_fiduci:69,balanc:[2,27,69,119],ball:[41,43,69,173,231],ballet:32,ballpark:69,bandwidth:69,banner:6,bar:[29,33,55,169,227,242],barrier:45,base:[0,2,3,6,7,11,13,17,18,20,22,25,27,28,31,32,36,37,38,39,40,42,43,44,45,46,48,49,55,63,67,69,73,74,76,77,78,79,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,142,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,164,165,166,167,168,169,170,185,190,198,205,208,214,215,233,242],base_data_read:[143,147,160],base_frame_nam:69,base_offset_rt_footprint:69,base_tfrom_sensor:69,basecli:[74,76,78,79,84,85,87,89,90,91,92,93,94,95,96,98,100,101,103,104,105,106,107,108,109,111,112,113,114,115,117,118,119,120,121,125,126,127,128,129,130,131,132,133,134,135,136,140,164,167],basedataread:[143,147,160],basenam:62,bashrc:188,basi:[24,44,58,66,69],basic:[20,25,35,37,48,60,61,66,67,69,139,144,146,170,171],basic_command_pb2:[22,63],basic_streaming_visu:[67,240],basicconfig:62,bat:65,batch:62,batch_siz:61,batteri:[4,10,13,22,29,40,45,53,65,66,67,69,115,119,172,174,175,176,177,178,179,180,181,182,183,184,185,217,227,241,245],battery_change_pose_command:119,battery_change_pose_feedback:69,battery_change_pose_request:69,battery_high_miss:22,battery_low_miss:22,battery_st:69,batterymissingerror:115,batteryst:45,battl:44,bblank_spot_v2_0_env:66,bddf:[34,67,68,75,141,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,191,200],bddf_download:[81,187],bddf_download_gui:187,bddf_read:187,beacon:[19,69],beacon_loss_count:69,beacons_receiv:69,beagl:214,bearer:37,beat:[2,27,28,29,31,32,69],beats_per_circl:[2,32],beats_per_cycl:[2,32],becaus:[6,14,16,17,18,24,25,27,42,43,44,45,61,62,64,65,67,69,117,213],becom:[10,19,27,37,53,67,69,83,118,213,227],becomeestopcommand:83,bedroom:213,been:[2,5,6,10,14,17,19,21,22,24,29,32,33,37,39,40,42,44,46,48,63,65,66,67,69,88,96,104,107,117,118,136,144,156,160,188,205,213,215,221,227,228,231],befor:[2,10,17,21,22,24,27,29,31,32,33,37,40,42,45,46,48,49,56,60,61,62,63,64,66,67,69,90,93,96,102,109,115,117,118,119,123,136,148,170,171,173,174,175,176,177,179,181,183,213,214,223,225,227,228,231,233,236,242],beforehand:31,began:32,begin:[2,6,7,10,17,18,20,22,25,27,28,29,31,32,33,35,40,42,46,69,228,233],behalf:[40,67],behav:[10,31,33,39,46,69,189,190,236],behavior:[7,9,10,13,15,19,24,27,31,32,45,50,58,63,64,69,79,100,119,123,139,170,192,196],behavior_fault:69,behavior_fault_id:[69,119],behavior_fault_st:69,behaviorfaulterror:[67,119],behaviorfaultst:45,behaviror:69,behind:[24,40,69,185],being:[2,6,7,8,15,19,24,25,27,28,29,31,33,36,42,44,45,46,48,58,62,63,65,66,67,69,88,93,96,100,126,139,208,213,231,233,236],believ:[17,21,45,69],belong:[25,69],below:[2,5,6,10,14,15,17,21,22,24,27,28,29,33,36,37,38,40,42,44,49,52,53,54,55,56,60,61,62,63,64,65,66,67,69,75,163,170,177,187,188,192,205,221,224,227,230,231,233,245],beneath:29,benefit:44,besid:173,best:[0,3,17,19,20,46,47,57,59,61,63,67,69,136,171,172,174,175,176,177,178,179,180,181,182,183,184,185,194,201,217,227,233,245],best_estim:69,best_obj:63,best_vision_tform_obj:63,beta29:[65,66],beta:[2,65,66,69],better:[6,10,17,29,37,52,58,63,64,67,69,214],bettter:69,between:[2,6,8,11,13,17,18,19,21,22,24,27,28,29,31,32,37,39,42,43,44,49,51,52,53,62,63,64,65,66,67,69,77,83,99,100,102,104,110,113,117,136,195,205,208,213,214,216,224,225,231,233,237],betwen:17,beyond:[0,3,6,31,38,45,66,67,69],bgr:63,bia:4,bias:43,bias_force_ewrt_bodi:69,bidirect:[44,69],big:[2,20,43,65,69,96],bigger:[63,69],bignum:[2,69],bim:[17,213],bin:[56,60,61,62,65,219,231,245],binari:[22,25,31,37,67,69,74,90,107,148,154,192],binary_descriptor:69,bind:[44,60],bind_al:61,bintrai:56,bit:[2,25,27,30,55,56,69,129,143,149,160,225],bitstatu:[69,127],black:[4,69,231],blackboard:[67,69,169],blackboard_vari:69,blackboard_variable_nam:69,bland:20,blank:[27,67,69,100],blend:[50,110],blink:227,bloat:69,blob:[25,67,69,90,107,192,218,221],blob_data:69,blob_spec:69,block:[10,14,20,25,27,31,40,44,46,62,63,67,69,84,86,88,94,100,104,115,118,119,123,135,139,142,147,149,155,156,160,190,211,225,227,228,231,233,242],block_entri:69,block_for_trajectory_cmd:[63,64],block_until_arm_arr:[63,64,67,119],block_until_complet:86,block_writ:[144,149],blockag:69,blocking_captur:[46,102],blocking_capture_funct:102,blocking_dock_robot:94,blocking_fault:69,blocking_go_to_prep_pos:94,blocking_stand:[66,119],blocking_undock:94,blockwrit:[144,149],blue:[2,17,27,29,69,213,216,227,240],blueprint:[17,213],blueprint_example_optim:213,bndbox:60,board:[43,49,50,67,127],bob:2,bob_magnitud:[2,32],bodi:[2,17,19,21,25,26,28,29,31,41,42,45,50,52,54,64,66,67,68,69,99,103,104,119,171,185,194,196,206,214,234,245],body_control:69,body_entry_slic:[2,27],body_euler_rpi:27,body_euler_rpy_angles_handl:72,body_exit_slic:[2,27],body_frame_nam:67,body_height:[66,119],body_hold_param:2,body_movement_statu:69,body_offset_from_hand:69,body_pitch_handl:72,body_po:[2,27],body_pos_handl:72,body_quat_w_handl:72,body_quat_wxyz:27,body_quat_x_handl:72,body_quat_xyzw:27,body_quat_y_handl:72,body_quat_z_handl:72,body_quaternion_wxyz_handl:72,body_quaternion_xyzw_handl:72,body_roll_handl:72,body_status_mov:69,body_status_settl:69,body_status_unknown:69,body_tform_odom:41,body_tform_payload:69,body_tracking_stiff:[2,27],body_x_handl:72,body_y_handl:72,body_yaw_handl:72,body_z_handl:72,bodycontrolparam:67,bodyexternalforceparam:119,bodyhold:2,bodymovementstatu:67,boiler:67,boilerpl:[60,62,63],bolt:54,bonu:64,bool:[2,31,69,82,91,93,98,99,102,103,104,113,118,121,123,135,136,139,162],bool_valu:69,boolvalu:[2,69,213],boot:[22,41,69,74],bootstrap:24,bosdyn:[2,17,24,25,27,28,30,37,39,44,46,58,60,62,63,65,66,69,72,73,74,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,164,165,166,167,168,169,172,174,175,176,177,179,180,181,182,183,185,187,190,191,192,194,205,207,208,210,211,213,214,215,216,221,223,225,227,228,229,240,242],bosdyn_sdk_robot:[21,102],bosdynclientbblank02:66,bosdyngraphnavloc:67,bosdyngraphnavst:[22,67],bosdynnavigaterout:67,bosdynnavigateto:22,bosdynpowerrequest:22,bosdynrecordev:67,bosdynrobotcommand:22,bosdynrobotst:22,boston:[0,1,19,20,25,31,44,46,47,49,50,51,55,65,66,67,68,69,71,75,84,104,124,141,142,145,170,187,198,227,236,242],bostondynam:[0,3,47,52,55,57,59,65,67,69,78],both:[2,6,7,13,14,16,17,19,21,24,25,28,29,30,32,35,38,40,45,46,48,53,62,64,66,67,69,73,82,96,175,188,214,225,227,232,242],boto3:188,bottom:[29,32,55,69,213],bottom_color:[2,32],bottom_land:69,bottom_left:[2,32],bottom_right:[2,32],bottommost:32,bound:[2,31,35,43,45,61,62,63,64,67,205,221,233,244],boundari:[10,29],bounding_box:69,bounding_box_properti:69,bourn:187,bourre:2,bourree_arm:[27,28],bourree_param:2,box:[14,29,31,33,35,43,45,60,61,62,63,64,65,67,69,205,221,231,233,244],bpm:[2,27,29,31],bpm_option:72,brace:69,branch:[13,17,69],break_on_success:136,breakag:69,breath:32,breath_max_period:2,breath_max_z:[2,32],breath_min_z:[2,32],bridg:[9,44,62,68,69,75,202,222],brief:69,bright:[20,67,69,128,232],brighten:32,bring:[6,33,64,69],broad:[25,44],broadcast:[44,98,227],broken:[29,42,67],brownian:32,browser:[60,61],bruno:31,brute:[20,67],bucket:[64,67,188,224],bucket_nam:188,buffer:[2,22,31,34,36,46,65,66,68,69,74,75,83,85,91,118,123,187,200,205,231],bug:[44,118],buggi:44,build:[4,17,18,19,22,37,43,44,58,63,64,67,69,101,114,119,136,189,190,205,221,224,225,227,231,242],build_body_external_forc:119,build_execute_choreography_request:74,build_image_request:101,build_inform:69,build_miss:219,build_on_command:[67,119],build_pc_request:114,build_rout:100,build_start_recording_state_request:74,build_synchro_command:[64,119],builder:67,built:[3,19,24,37,43,44,49,64,65,67,69,74,205,222,231],bulk:53,bumper:33,bunch:[62,64],bundl:17,bus:[53,69],butt:[2,32],butt_circle_param:2,buttcircl:2,button:[28,29,33,36,49,53,65,67,69,96,171,172,174,175,176,177,178,179,180,181,182,183,184,185,204,217,220,227,241,245],bypass:[10,69,190,225,227,229,242],bytes_data:46,bytes_delet:69,bytes_per_point:69,bytesio:[62,66],bytestr:[2,69],cabinet:[67,69,173],cabl:[17,215,216],cach:[17,19,24,45,69,75,100,118,122],cache_directori:137,cad:[51,69],cadenc:[2,104],cal:[69,135],calcuat:69,calcul:[24,29,41,69,213],calibr:[7,37,63,67,135],call:[6,13,17,18,19,20,22,28,36,43,44,46,48,58,60,61,62,63,64,65,66,67,69,77,84,88,93,94,97,102,104,107,111,112,113,114,115,118,119,122,123,125,126,127,128,129,130,131,132,133,134,135,136,148,151,154,158,162,173,190,191,198,213,214,216,221,223,227,228,236],call_async:84,callabl:[82,104,122],callback:[9,23,42,48,62,67,69,84,104,203,211],caller:[44,69,84,242],cam:[3,22,35,38,45,46,49,68,69,75,125,126,127,128,129,130,131,132,133,134,199,202,208,227,236],came:25,camera1:242,camera2:242,camera:[6,7,8,24,29,36,40,41,43,45,60,62,63,67,68,101,102,126,129,135,205,208,217,220,221,226,231,232,233,236,242],camera_calibration_command:135,camera_calibration_command_async:135,camera_calibration_feedback:135,camera_calibration_feedback_async:135,camera_interfac:102,camera_interface_object:46,camera_model:[63,69],camera_result:69,camera_sourc:69,camerabaseimageservic:[46,102],cameracalibrationcalibrationerror:135,cameracalibrationcommand:69,cameracalibrationfeedback:69,cameracalibrationinternalerror:135,cameracalibrationpowererror:135,cameracalibrationresponseerror:135,cameracalibrationrobotcommanderror:135,cameracalibrationtargetnotcenterederror:135,cameracalibrationtimedouterror:135,cameracalibrationusercancelederror:135,camerainterfac:102,can:[0,2,3,6,7,9,10,11,12,13,14,15,16,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,35,36,37,38,39,40,41,42,43,44,45,46,48,49,50,51,52,53,54,55,56,58,59,60,61,62,63,64,66,69,73,74,78,84,85,87,88,93,97,99,100,101,102,104,110,113,117,118,119,120,123,131,135,139,149,164,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,199,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,240,241,242,243,244],can_power_command_request_cycle_robot:69,can_power_command_request_off_robot:69,can_power_command_request_payload_port:69,can_power_command_request_wifi_radio:69,cancel:[29,33,36,41,46,67,69,84,85,86,87,88,96,97,100,119,135,236],cancel_acquisit:[85,87],cancel_acquisition_async:[85,87],cancel_acquisition_request:86,cancel_check:[46,88],cancel_interv:88,cancelacquisit:[36,69,88,236],cancelacquisitionrequest:88,cancelacquisitionrespons:88,cancellationfailederror:[85,87],candid:[69,130],cannot:[6,21,27,29,31,45,46,62,67,69,74,78,84,100,101,102,113,114,115,117,122,166,190,206,225,227,236,242],cant:69,cap:[52,53,73,228],cap_v4l:242,capabl:[0,6,8,19,22,38,43,45,50,60,66,67,69,83,85,87,88,190,236,242],capac:[4,8,52],capacit:53,capit:[27,55,227,228],capsul:69,caption:[62,63],captur:[13,17,21,23,34,35,37,41,43,45,60,62,63,67,69,83,85,87,88,89,102,125,171,208,217,227,231,233,242],capture_func:102,capture_imag:60,capture_param:69,capture_period:102,capture_period_sec:102,capture_tim:102,captureactionid:[86,87,89],captureparamet:102,car:21,care:[6,39,42,46,50,61,63,64,65,66,67,69,177,231],carefulli:[21,40,69],carpet:8,carri:[6,8,12,21,22,23,48,52,63,64,67,69],carriabl:[67,69],carriable_and_stow:67,carry_cmd:63,carry_st:69,carry_state_carri:69,carry_state_carriable_and_stow:69,carry_state_not_carri:69,carry_state_overrid:69,carry_state_unknown:69,cartesian:69,cartesian_veloc:69,cast:[61,69],cat:[37,49,55],catalina:59,catalog:12,categori:[29,31,40,69],category_anim:2,category_annot:2,category_arm:2,category_bodi:2,category_dynam:2,category_index:62,category_kneel:2,category_light:2,category_mpc:2,category_step:2,category_transit:2,category_unknown:2,caught:[46,50],caus:[2,6,10,17,21,27,31,40,42,44,45,46,53,61,66,67,97,104,119,131,171,172,178,184,185,195,227],cause_fal:69,cause_hardwar:69,cause_lease_timeout:[67,69],cause_unknown:69,caution:[32,69],caviti:65,ccolor:32,ceil:215,cell:[44,45,69],cell_format:69,cell_format_float32:69,cell_format_float64:69,cell_format_int16:69,cell_format_int8:69,cell_format_uint16:69,cell_format_uint8:69,cell_format_unknown:69,cell_siz:69,cell_value_offset:69,cell_value_scal:69,celsiu:69,center:[2,6,26,27,29,30,31,32,41,52,54,56,63,66,69],center_point:69,center_pt:69,center_px_i:63,center_px_x:63,centerlin:32,centerpoint:2,cert:[66,82,122,130],cert_resource_glob:122,certain:[2,10,20,22,24,29,31,40,42,45,50,56,67,69,170,212],certainli:97,certif:[44,69,97,122],cfg:177,cha:[27,28,31,72,73,74,171],chain:[17,18,44,69,213],chalk:[6,7,67],challeng:[39,69,96],chanc:[17,67,69],chanel:154,chang:[0,2,10,13,14,16,18,19,21,24,29,31,32,41,42,44,45,48,49,55,56,61,62,63,64,65,66,69,74,93,96,110,113,126,133,140,177,188,190,204,205,208,213,225,227,231,241,242,243,245],changeset:[66,69],changeset_d:[66,69],channel:[25,31,35,37,44,46,66,67,69,75,81,84,90,97,107,118,122,125,142,145,147,148,149,154,158,159],channel_glob:69,channel_nam:[46,69,154,157,159],channel_name_to_series_decriptor:154,channel_prefix:123,channel_read:157,channelcredenti:82,chaotic:32,charact:[22,58,67,86,228,242],characterist:171,charg:[4,13,15,22,29,45,53,64,66,67,69],charge_percentag:69,charger:[4,67],chart:61,chassi:54,cheap:25,check:[13,15,16,19,22,24,28,29,31,33,39,40,42,44,45,46,49,56,61,62,65,66,67,68,69,74,75,85,87,88,94,96,99,104,105,115,118,119,121,136,139,170,190,219,227,236,242],check_edges_for_collis:69,check_in:96,check_in_async:96,check_in_at_level:96,check_in_at_level_async:96,checkbox:29,checkin:[66,69],checklist:[61,62],checkpoint:[61,62],checkpoint_dir:61,checksum:[25,69,143,146,160],checksum_non:69,checksum_num_byt:69,checksum_typ:69,checksum_type_non:69,checksum_type_sha1:69,checksum_type_unknown:69,checksumerror:146,chees:242,chicken:[2,32],chicken_head_param:2,child:[22,41,67,69,99,169],child_frame_nam:99,child_to_parent_edge_map:[41,69,119],childframeintre:99,children:[22,69,169],chmod:[30,49],choic:[6,69],choos:[16,18,29,37,43,46,56,67,69,101,205,213,223],choosen:2,choreograph:[2,26,28,31,73,74,203,238],choreographi:[2,3,27,30,33,57,65,70,72,73,171],choreography_log_to_animation_fil:74,choreography_nam:74,choreography_seq:74,choreography_sequ:73,choreography_sequence_nam:2,choreography_sequence_pb2:[72,74],choreography_start_slic:2,choreography_starting_slic:[2,74],choreographycli:74,choreographysequ:74,choreographyservic:74,choreographystatelog:[28,31,74],choreogrpahi:74,chosen:[44,46,69,100,119,242],chunk:[2,67,68,69,84],chunk_messag:84,circl:[2,32,66,172],circle_region:117,circuit:[53,66],circuitri:53,circular:[69,117],circumst:[15,69],ckpt:61,claim:[42,69,104],clamp:[8,53,69],clap:[2,31,32],clap_dist:[2,32],clap_param:2,classif:67,classnam:58,claw:69,claw_gripper_close_command:119,claw_gripper_command:69,claw_gripper_feedback:69,claw_gripper_open_angle_command:119,claw_gripper_open_command:119,claw_gripper_open_fraction_command:[64,119],clean:[15,55,67,86,123,139],clean_filenam:86,cleanli:[46,88,123,139],cleanup:[88,123,139],cleanup_request:88,clear:[2,11,14,15,18,40,45,46,67,69,93,98,100,102,119,127,137,195,214,229],clear_all_payload_fault:[69,98],clear_all_service_fault:[69,98],clear_behavior_fault:119,clear_behavior_fault_async:119,clear_bit_ev:127,clear_bit_events_async:127,clear_buff:69,clear_fault:102,clear_graph:100,clear_graph_async:100,clear_robot:122,clear_service_fault:98,clear_service_fault_async:98,clearabl:[40,45,69],clearanc:[8,178,184,185],clearbehaviorfault:69,clearbitev:69,clearer:67,clearfailederror:137,cleargraph:[18,69],clearservicefault:69,clearservicefaultrespons:98,cli:[49,139,188,227,235],cli_auth:139,cli_login_prompt:139,click:[7,27,29,30,33,49,55,60,67,178,184,223,224,225,242],client:[0,2,3,9,12,13,16,17,18,20,21,22,23,24,26,27,28,30,36,37,38,39,40,41,42,44,45,46,48,56,57,58,59,60,62,63,64,65,66,69,70,72,73,74,76,77,78,79,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,109,110,112,113,114,115,116,117,118,119,120,121,122,123,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,171,172,173,174,175,176,177,179,180,181,182,183,185,189,190,191,192,193,194,195,198,201,205,207,208,210,211,213,214,223,225,227,229,234,239,240,242],client_error:69,client_nam:[66,69,104],client_name_func:116,client_name_prefix:122,client_rx:69,client_start_tim:74,client_tx:69,clientcancelledoperationerror:97,clientcontext:[88,102],cliff:[67,69],climb:[52,69,117,171],clock:[2,24,44,46,58,66,69,74,81,83,100,102,104,107,118,136,162,192,237],clock_identifi:[69,94,136],clock_skew:[69,136],clockwis:[2,32,69,177],clone:[29,65],close:[2,6,14,16,17,18,20,21,27,29,31,61,62,64,67,69,107,109,144,148,173,195,205],closer:[32,69],closest:[29,69,110,172,174,175,176,177,178,179,180,181,182,183,184,185,217],closest_yaw_only_quaternion:110,closet:213,closur:69,cloud:[3,17,21,41,44,68,69,75,100,117,197,199,213,216,221],cloud_upload:188,cloudcompar:[17,215],cloudi:69,clutch:69,cmd:66,cmd_durat:100,cmd_id:[63,64,119],cmd_respons:63,cnn:221,coast:69,cockpit:[47,56],coco:[231,233],code:[0,4,6,9,12,16,20,22,25,38,40,44,45,46,48,49,58,60,62,63,64,65,66,71,75,81,83,84,90,104,105,106,112,113,114,124,141,142,162,163,164,170,188,197,198,205,208,211,214,231,242],code_internal_server_error:69,code_invalid_request:[62,67,69],code_ok:[66,69],code_unspecifi:69,codec:[67,242],coefficeint:69,coeffici:[67,69,117],coher:69,coincid:27,col:[63,69,102],collaps:66,collect:[9,19,20,23,24,36,38,40,58,60,64,67,69,74,84,88,101,102,114,190,213,216,231,233,236],collector:53,collid:[6,69],collis:69,collision_check_height_vari:69,collision_check_param:69,collision_check_robot_radiu:69,color:[17,27,29,31,32,45,63,67,126,216,230,231,240],color_gray2bgr:63,color_gray2rgb:62,colormap:[67,126],colormap_greyscal:69,colormap_jet:69,colormap_unknown:69,column:[24,29,35,46,65,69],com:[0,3,27,47,52,55,56,57,59,65,66,67,69,78,205,221,231],com_height:[2,32],com_po:[2,27],com_pos_handl:72,com_pos_rt_payload:69,com_x_handl:72,com_y_handl:72,com_z_handl:72,combin:[2,5,6,21,22,24,27,29,31,32,36,37,44,45,48,49,52,53,64,66,67,119,215,228,245],combo:[44,78,113],come:[2,10,17,32,39,44,49,52,56,62,64,65,66,67,69,119,129],comfort:29,comm:[10,16,22,37,66,67,69,115,195,198,225],comma:233,command:[2,3,5,6,7,10,14,15,16,17,18,22,23,24,29,30,32,33,36,40,42,43,46,50,55,56,58,60,63,64,68,74,75,76,81,94,95,96,100,108,115,118,135,169,170,171,172,177,178,180,181,183,184,187,188,189,190,192,196,197,198,205,208,209,210,212,215,216,217,218,219,222,224,225,226,227,228,229,230,231,232,233,235,236,237,238,239,240,242,245],command_abort:69,command_cancel:69,command_cli:[63,64,66,115,119],command_dict:83,command_id:[67,69,94,100],command_lin:[83,230],command_revert_c:69,command_start:69,command_unknown:69,commandexpirederror:100,commandfailederror:[94,119],commandfeedbackrequest:45,commandinprogresserror:115,commandrespons:45,commandtimedouterror:[67,115,118,119],commenc:66,comment:[44,64,67,69,83,90,91,107,118,192,218],commerci:19,commit:[67,69],common:[2,6,19,29,44,45,46,56,58,60,61,62,65,66,67,69,74,75,76,78,79,85,87,89,90,91,92,93,94,95,96,97,98,100,101,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,125,126,127,128,129,130,131,132,133,134,135,136,139,140,142,145,162,164,167,231,233,242,245],common_header_error:84,common_lease_error:84,commonerror:[44,62,123],commonli:[13,22,41,45,65,118,242],comms_out:189,comms_stat:69,comms_test:189,commsstat:45,commun:[0,3,10,22,24,26,38,39,42,44,45,46,48,49,52,55,57,62,65,66,69,79,82,85,87,88,90,91,92,93,94,95,96,98,100,101,102,104,106,107,109,111,112,113,114,115,118,119,121,122,136,140,164,189,208,225,227,231,233,236,242,245],compact:[44,66],compar:[13,18,20,42,63,67,104,120],compare_eq:69,compare_g:69,compare_gt:69,compare_l:[22,69],compare_lt:69,compare_n:69,compare_result:104,compare_result_to_lease_use_result_statu:104,compare_unknown:69,compareresult:104,comparison:[22,69,104],compart:[172,174,175,176,177,178,179,180,181,182,183,184,185,217],compat:[24,44,51,55,65,66,67,69,82,122,139],compens:[2,32,67],compil:[22,36,38,58,61,67,69,123,139,164,166,228,235],compilationerror:164,compileerror:166,complet:[2,4,8,13,15,16,20,22,24,27,28,31,32,33,36,41,42,44,46,48,49,53,58,61,63,64,66,67,69,77,86,88,94,97,100,102,104,111,117,135,170,203,214,227,236,242],complete_after_acquir:69,complete_after_sav:69,complete_unknown:69,completion_behavior:69,complex:[9,17,47,69,170,175],compli:[6,38,69],compliant:6,complic:[22,84],compliment:39,compon:[9,28,38,47,48,49,50,53,66,67,69,199,236],compos:[2,13,31,45,119],composit:69,compositor:[67,68,124,230],compositor_pb2:126,compositorcli:126,compositorservic:[67,126],compress:[45,133,242],compress_live_point_cloud:69,compress_point_cloud:69,compression_gzip:69,compression_non:69,compression_unknown:69,compression_zstd:69,compris:[36,67,69],comput:[9,17,19,20,21,24,25,27,29,31,33,35,41,44,45,46,60,62,63,64,65,68,69,75,83,102,110,115,119,143,146,189,202,205,208,222,224,225,227,228,237,242],computation:[43,58],compute_stand_location_and_yaw:[63,64],computer_serial_numb:[24,66,69],con:65,concaten:[2,69],concatent:69,concatin:69,concav:69,concept:[0,5,13,17,34,58,59,64,66,170,213,228],conceptu:[0,58,67],conclud:23,conclus:17,concurr:62,condens:55,condit:[18,19,22,29,39,66,100],cone:15,conf:63,conf_msg:63,confid:[16,60,62,63,69,221,231],confidence_bound:69,confidence_dogtoi:63,confidence_person:64,config:[48,61,62,69,94,96,112,113,210,224],configmismatcherror:96,configrang:94,configur:[6,15,20,27,29,32,36,44,45,46,47,55,56,65,67,69,79,83,96,102,121,122,130,135,177,188,189,190,201,205,208,221,225,228,229,231,232,233,242,245],configure_async:79,configure_error:79,configurerespons:79,confirm:[39,42,225,227],conflict:[49,224],conform:50,confus:67,congest:[67,69,133],congratul:[64,65],conjunct:52,connect:[3,13,14,15,17,18,19,21,24,26,28,29,30,40,44,47,48,49,50,51,53,55,56,57,60,62,65,66,67,69,82,97,100,102,118,130,199,208,214,216,225,228,231,233,236,242,245],connected_tim:69,connector:[15,44,50,53],consecut:[24,32,214],consequenti:69,conserv:69,consid:[2,6,17,20,21,29,40,42,45,50,52,53,58,62,65,69,84,100,104,177,231,233],consider:[44,52],consist:[0,2,13,17,19,21,22,24,25,26,27,29,31,46,58,65,69,74,77,109,215,216,240],consol:[24,48,49,55,69,113,139,228],const_proto:169,constaint:14,constant:[27,31,40,62,63,67,69,119,146,163],constantli:[231,233],constantresult:22,constantvalu:169,constrain:[2,6,16,19,69,119,196],constrained_manipulation_command:119,constrained_manipulation_feedback:69,constrained_manipulation_request:69,constraint:[2,7,63,69,100,109,117],constraintfaulterror:100,constraintviolationerror:109,construct:[19,29,49,64,67,69,74,99,117,119,173],constructor:[46,67,104],consum:[40,69,227],contact:[2,5,6,7,16,27,28,31,45,50,51,52,55,65,67,68,75,78,196],contact_handl:72,contact_lost:69,contact_mad:69,contact_unknown:69,contain:[2,10,13,17,18,19,21,22,25,27,28,29,31,35,36,37,39,41,42,45,46,47,53,58,60,65,66,67,69,73,74,82,84,85,87,88,100,102,104,106,109,111,116,118,119,122,137,145,149,152,171,189,190,192,213,214,221,224,227,228,231,242],content:[25,46,56,61,65,69,73,74,148,192],content_typ:[69,148],context:[19,31,37,48,62,69,88,102],continu:[2,6,8,10,13,16,19,42,45,46,49,53,62,63,64,66,67,69,74,97,100,102,104,122,205,206,220,224,227,228,240],continue_recording_dur:2,continue_session_id:74,contour:31,contralater:2,contrast:[41,215],control:[0,2,3,5,6,7,9,10,13,15,18,19,22,23,24,28,31,32,36,42,44,45,50,56,57,59,63,64,65,66,69,78,83,94,104,119,126,133,170,171,172,175,178,189,196,203,204,205,213,220,232],controlcent:56,controls_annot:2,controls_arm:2,controls_bodi:2,controls_gripp:2,controls_leg:2,controls_light:2,controls_opt:72,convei:[2,32],conveni:[33,61,62,65,69,118,121,139],convent:[64,67,142,145,213],converg:[69,109],convers:[62,73,85,136,162],convert:[24,27,30,31,41,44,46,61,62,63,64,67,69,72,73,74,84,86,99,110,118,119,136,140,162,169,192,240],convert_animation_file_to_proto:73,convert_timestamp_from_local_to_robot:162,convert_to_tensor:62,convolut:61,coord:[67,69],coordiant:69,coordiat:69,coordin:[7,12,17,35,36,41,43,45,62,63,66,67,69,101,104,110,119,126,202,205,213,216,221,240],copi:[2,33,49,56,60,61,62,64,69,76,88,95,108,119,205,224,228,231],copyfrom:[22,46,63,64,213],copyright:[65,66],core:[3,24,47,48,49,58,65,66,67,69,70,189,190,222,224,225,227,231,242],corner:[17,20,41,50,51,55,63,69,213],correct:[2,7,24,31,42,64,65,67,69,74,119,140,190,213,221,225,227,228,236,242],correctli:[20,27,40,45,49,51,60,63,65,66,67,69,100,205,224,227,228,236,239,242,244],correl:[2,21,63,240],correspond:[2,14,22,31,33,36,37,38,43,45,49,66,67,69,100,104,109,127,148,149,169,208,215,216],corridor:[19,21],cos:213,cost:[67,69,213],could:[2,13,16,24,25,29,31,32,41,44,45,58,61,62,64,65,67,69,74,90,91,97,99,102,104,107,117,119,122,140,164,166,169,213,214,228,236,242,244],couldn:140,couldnotcreatewaypointerror:117,count:69,countabl:69,countdown:33,counter:[60,69],counterclockwis:[69,177,208],coupl:[46,55,67,69,180,227,242],cours:39,cov_rzrz:69,cov_xi:69,cov_xx:69,cov_xz:69,cov_yi:69,cov_yx:69,cov_yz:69,cov_zi:69,cov_zx:69,cov_zz:69,covari:[67,69],cover:[0,21,24,50,55,59,61,62,65,66,69,119,170,196,203],cpp:242,cpu:[25,53,61,62,69,129,221],cpython:205,crank:[67,69,173],crash:[40,67,69,131,171,229],crashbar:7,crawl:[2,69,245],crawl_param:2,creat:[2,3,4,11,13,14,18,19,21,24,25,27,28,29,30,31,32,36,38,41,42,44,45,48,51,52,58,60,61,62,63,65,67,69,73,74,78,82,84,85,86,88,102,104,110,117,118,119,120,122,123,139,152,190,201,206,208,213,214,219,220,223,224,225,227,228,231,233,237,239,240,241,245],create_arm_joint_trajectory_point:119,create_capture_thread:[46,102],create_category_index_from_labelmap:62,create_edg:117,create_edge_async:117,create_insecure_channel:82,create_move_info_proto:73,create_new:104,create_robot:[60,62,63,66,122,213],create_secure_channel:82,create_secure_channel_cr:82,create_standard_sdk:[60,62,63,66,122,213],create_strict_vers:120,create_subleas:104,create_waypoint:117,create_waypoint_async:117,created_edg:69,created_waypoint:69,createedg:[17,18,69],createwaypoint:[17,18,69],createwaypointrespons:67,creation:[42,67,69,82,228],creation_func:122,creation_tim:69,cred:82,credenti:[24,30,48,49,55,65,66,67,69,78,82,113,118,221,228,229],credit:[65,66],critic:[24,37,44,69,100],crop:69,cross:[17,21,22,29,32,64,69,117],crosslink:50,crtical:69,crtl:55,crude:232,cryptographi:67,csq:238,csv:[69,189,221],ctl:83,ctrl:[29,60,225,234,242],cubic:69,cuda:[60,61,64,221],cuda_visible_devic:61,cudnn:61,cudnn_status_internal_error:61,cumul:[21,69],cup:69,curl:37,current:[2,4,6,10,11,14,15,16,17,18,19,21,24,25,27,29,31,32,33,37,39,41,42,45,48,49,52,53,61,63,64,66,67,69,74,83,88,94,96,100,104,110,117,118,119,121,122,125,126,134,135,136,149,160,162,164,177,187,188,189,190,192,206,208,209,210,214,226,227,228,234,236],current_mod:69,current_st:[63,69],current_state_str:63,current_tim:63,curv:67,curvatur:17,custom:[19,21,22,24,27,28,32,35,36,38,43,44,55,64,67,69,79,84,85,123,139,226],custom_metadata:35,customiz:[28,29],cut:[2,25,39,66,67,69,96,118,204,205,221,232,234],cut_immedi:[66,118],cut_power_timeout:[66,69],cv2:[60,62,63],cvtcolor:[62,63],cycl:[2,22,32,53,66,67,69,99,113,115,131,136,214,227,228],cycle_pow:131,cycle_power_async:131,cyclepow:[67,69],cylind:69,cylindr:[6,69],cylindrical_veloc:69,daemon:56,dag:[69,99],dai:[67,187,190,192],damag:[6,20],damp:69,danc:[2,27,28,29,31,32,33,66,67,74,238],dance_directori:[27,28],dance_frame_id:2,dancemov:2,danger:10,daq:[37,69,85,87,224],daq_upload_docking_callback:224,daqdockinguploadservic:224,dark:[21,69],darken:32,darker:21,data:[2,3,4,9,13,15,18,20,21,22,23,24,28,38,40,41,44,45,47,48,49,53,60,61,62,63,64,66,68,69,74,75,77,81,83,84,99,100,101,102,107,111,114,117,118,123,125,129,137,142,144,145,146,149,151,154,155,156,157,158,159,170,208,214,215,216,221,227],data_acq_cli:86,data_acquisit:85,data_acquisition_cli:86,data_acquisition_download:190,data_acquisition_exampl:190,data_acquisition_help:86,data_acquisition_pb2:[46,86,88],data_acquisition_plugin:87,data_acquisition_plugin_servic:[46,75,88,190],data_acquisition_plugin_service_pb2_grpc:88,data_acquisition_servic:75,data_acquisition_stor:89,data_acquisition_store_pb2:[86,123],data_acquisition_store_servic:75,data_block_s:156,data_buff:[90,107,191],data_buffer_pb2:123,data_buffer_servic:75,data_buffer_statu:69,data_buffer_total_byt:69,data_captur:69,data_chunk_byte_s:[84,164],data_collect_fn:[46,88],data_error:[69,88],data_id:[35,46,69,88,89],data_id_future_pair:88,data_identifi:87,data_nam:[35,46,69],data_queri:69,data_read:[147,151,152,154,155,158],data_sav:69,data_servic:[75,91],data_sourc:69,data_store_cli:86,data_timestamp:85,data_writ:[148,153,156,159],dataacquisit:[67,86],dataacquisitioncap:[67,88],dataacquisitioncli:[35,67,85,86],dataacquisitioncommand:83,dataacquisitionobject:37,dataacquisitionplugincli:87,dataacquisitionpluginservic:[36,46,87,88,190,236],dataacquisitionpluginserviceservic:88,dataacquisitionrequestcommand:83,dataacquisitionresponseerror:85,dataacquisitionservic:[85,87],dataacquisitionservicecommand:83,dataacquisitionstatuscommand:83,dataacquisitionstor:[69,190],dataacquisitionstorecli:[86,88,89],dataacquisitionstorehelp:[46,88],dataacquisitionstoreservic:89,databas:[60,69,129],datablock:69,databuff:[67,69,86],databuffercli:[67,90,123],databuffercommand:83,databufferservic:[67,90],datacaptur:46,datachunk:2,datadescriptor:[25,149,160],dataerror:[46,88,146],datafil:[151,154,155,157,158],dataformaterror:[146,148,156,159],datagram:69,dataid:88,dataidentifi:[46,87,88,89],dataqueri:91,dataqueryparam:[86,89],dataread:[143,146,147,151,152,154],dataservic:[67,91],dataservicecli:91,dataservicecommand:83,dataset:[67,231,233],datastor:86,datatyp:69,datatypedescriptor:[147,154,158],datawrit:[146,148,149,156],date:[49,65,69,190,224],date_tim:162,datetim:[69,162],datetimeparseerror:162,david:67,dawn:162,daylight:69,db25:53,dbu:56,ddthh:190,deactiv:[65,231,233,245],dead:[44,62,69],deadlin:[16,66,69,88,136],deadline_exceed:66,deal:14,debug:[30,37,42,44,45,46,48,62,65,66,67,69,84,102,139,192,213,236,244],debug_error_str:66,debug_image_filenam:62,deceler:69,decid:[16,21,43,69],decim:69,decis:[17,45,225],declar:[18,21,38,58,69],decod:[46,69,102,122,208],decode_format:102,decode_token:122,decompress:242,decor:84,decreas:[69,233],dedic:[49,53,67],deduploggingmessag:139,deem:139,deep:[43,61,221],deeper:59,def:[17,21,44,46,60,62,63,64,88,188,213,227],default_app_token_path:139,default_gatewai:227,default_paramet:2,default_port:62,default_region:[69,117],default_rpc_timeout:67,default_service_nam:[21,38,40,48,60,62,63,66,74,76,78,79,85,87,89,90,91,92,93,94,95,96,98,100,101,103,104,105,106,107,108,109,111,112,113,114,115,117,119,120,121,125,126,127,128,129,130,131,132,133,134,135,136,140,164,167,213],default_time_sync_interval_sec:136,default_valu:27,defaultdict:84,defin:[2,6,12,13,18,19,21,22,23,24,26,27,28,31,32,36,37,38,40,44,55,58,62,63,64,67,69,73,88,90,93,113,123,139,148,188,190,228,236],defineblackboard:22,definit:[0,22,26,27,32,38,41,44,45,58,65,66,67,69,73,75,92,93,112,118,163,225,228],deg2rad:213,degrad:127,degre:[4,8,32,58,63,67,69,132,208],degred:69,del:60,delai:[29,31,44,67,69,208,231,233],delay_in_second:29,deleg:[36,42,67,69,137],delet:[37,45,60,61,64,67,69,125,129,137,140,192,230,243],delete_async:129,delete_data_pag:91,delete_data_pages_async:91,delete_sound:125,delete_sound_async:125,deleted_timestamp:69,deletedatapag:69,deletesound:69,deliber:69,deliv:[64,117],demo:[22,67,69,172,178,184,185],demonstr:[11,24,31,32,40,44,46,58,59,61,66,67,172,173,175,178,180,182,184,189,190,192,193,194,197,205,207,208,209,210,211,212,213,214,217,218,219,223,225,228,229,230,237,239,240,243,244],deni:[45,65,97],denial:69,denot:[41,69,97],dens:[27,28,46],depend:[6,10,19,20,21,22,24,25,27,29,30,32,42,48,49,58,60,62,65,69,119,123,197,221,224,231,233,242],depict:[17,22],deploi:[32,44,48,49,63,65,69,242],deploy:[44,49,67],deprec:[2,44,69,78,93,107,115,119,122],depress:[171,172,174,175,176,177,178,179,180,181,182,183,184,185,217],depth:[7,8,24,30,45,60,63,66,67,69,101,102,199,221],depth_in_visual_fram:[45,67,207],depth_scal:69,dereferenc:69,deregist:[48,69,96],deregister_async:96,deregisterestopendpoint:69,deregistr:69,deriv:[69,97,104,123,139],desc_block:144,descend:21,descprit:2,descrb:69,describ:[2,4,13,17,19,20,22,24,25,27,28,31,32,34,36,37,41,42,44,45,49,52,58,61,65,66,67,69,88,101,110,117,119,123,129,135,143,148,149,162,190,206,208,217,221,224,225,227,232,233,235,238,242,244,245],descrin:221,descript:[2,11,15,16,18,22,24,25,26,27,28,29,31,40,45,46,48,53,56,65,66,69,90,118,122,162,192,228],description_opt:72,descriptor:[25,69,143,149,154,160,169],descriptor_file_offset:69,descriptor_index:149,descriptorblock:[25,144,160],deseri:[2,44,67,69,151,157,158],desert:20,design:[6,20,22,23,25,32,37,38,40,44,47,51,52,65,119,178,184],desir:[2,6,17,19,29,31,32,42,46,48,49,55,56,63,67,69,96,102,109,119,131,166,213,214,220,225,227],desired_wrench_odom_fram:69,desktop:[55,56,67],destin:[11,14,16,21,22,55,67,69,73,100,169,190,214,224,236],destination_fil:188,destination_fold:86,destination_typenam:169,destination_waypoint_id:[69,100],destination_waypoint_tform_body_go:[67,69,100],destroi:102,detach:[67,69,113],detach_payload:113,detach_payload_async:113,detail:[0,5,6,12,13,17,20,21,31,35,37,38,39,44,46,47,48,51,52,55,56,58,59,61,65,66,67,69,78,88,93,94,100,104,119,126,136,166,173,188,201,228],detect:[15,17,19,20,41,43,45,46,49,60,61,62,63,67,69,117,170,190,202,205,212,216,221,222,227,232,242],detect_fn:62,detected_gaug:69,detection_box:62,detection_class:62,detection_covari:69,detection_covariance_reference_fram:69,detection_scor:62,detector:[43,61,67,69,202,231,233],determin:[13,14,15,18,19,20,21,24,25,27,29,32,36,39,40,41,42,45,46,48,60,62,67,69,74,83,84,85,100,101,104,110,117,139,190,205,213,225,227,228,231,237,242],dev:[0,3,47,56,57,59,67,242],develop:[0,3,9,11,19,26,28,37,40,44,45,46,48,49,50,52,55,57,58,59,65,69,104,119,122,170,190,236],deviat:[18,69],devic:[6,40,47,50,55,66,67,69,102,127,130,131,226,228,242],device_nam:242,diagnos:[45,55,69],diagon:69,diagram:[3,22,33,36,44,46,63,231,233],dialog:[28,60],diamet:[54,69,177],diari:37,dict:[85,86,87,99,118,143,148,149,231],dictat:[18,69],dictionari:[28,41,73,83,119,122],did:[2,37,61,62,64,66,67,69,96,97,104,111,115,118,217,225],didn:[60,61,63,65],dies:25,diff:65,diff_vec:64,differ:[2,6,10,14,16,17,19,20,21,22,24,25,26,27,28,29,31,32,33,36,40,41,42,44,45,46,48,49,55,56,58,60,61,62,65,66,67,69,73,74,83,85,87,100,101,102,104,106,114,119,136,180,189,190,196,198,200,202,203,206,208,213,214,216,218,225,227,236,240,242],different_epoch:104,different_resourc:104,differenti:[35,58,67],difficult:[2,19,21,27,40,58,60,63,67,213,242],diffus:50,digest:[25,69],digit:[213,215],dimens:[2,27,41,45,62,67,69,148,156],dimension:[69,173],dir:[24,60,61,62,66,83,190,221,227,242],dir_hint:119,dir_reg_cli:[38,40,93],direct:[2,3,5,6,7,9,17,21,24,27,32,55,58,63,64,67,69,93,117,119,172,190,216,220,221,227,228,241],direction_constraint:[69,117],direction_constraint_forward:69,direction_constraint_no_turn:69,direction_constraint_non:69,direction_constraint_revers:69,direction_constraint_unknown:69,direction_hint:69,directli:[6,16,17,22,28,29,30,31,32,40,44,62,64,65,67,69,85,87,104,119,213,222,225,227],directori:[17,27,28,30,35,36,40,43,48,60,61,62,64,65,66,67,68,75,83,98,100,101,102,111,117,118,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,189,190,194,198,208,213,214,215,216,217,221,224,225,227,228,229,231,233,235,236,242,245],directory_cli:62,directory_modif:[67,193],directory_nam:[38,40,93,236],directory_registr:[62,93],directory_registration_cli:[62,93],directory_registration_servic:75,directory_servic:75,directory_service_author:118,directory_service_nam:118,directory_to_save_miss:220,directorycli:[62,92],directorycommand:83,directorygetcommand:83,directorylistcommand:83,directorymanag:69,directoryregistercommand:83,directoryregistrationcli:[38,40,62,93],directoryregistrationkeepal:[38,40,93],directoryregistrationresponseerror:93,directoryregistrationservic:[24,66,67,93],directoryresponseerror:92,directoryservic:[24,66,92],directoryunregistercommand:83,dirnam:62,disabl:[6,29,33,53,60,62,68,69,75,96,115,119,198,205,208,220,227,228],disable_alternate_route_find:[69,169],disable_body_force_limit:69,disable_directed_explor:[69,169],disable_force_on_contact:69,disable_ir_emiss:194,disable_nearmap_cliff_avoid:69,disable_rpc_log:69,disable_vision_body_obstacle_avoid:69,disable_vision_foot_constraint_avoid:69,disable_vision_foot_obstacle_avoid:69,disable_vision_foot_obstacle_body_assist:69,disable_vision_negative_obstacl:69,disable_walk:69,disallow:69,disallow_non_stairs_pitch_limit:69,disallow_stair_track:69,disambigu:[58,69],discharg:[53,69],disconnect:[16,29,33,67,69,99,214,226],discourag:[65,190,225,227,242],discov:[24,41,44,69],discover:69,discoveri:[24,69],discrep:69,discret:192,discuss:[66,213],disjoint:[17,99],disk:[25,36,37,60,67,69,213],displac:10,displacedleaseerror:104,displai:[2,7,17,24,27,29,31,36,46,48,64,65,66,67,69,126,208,213,216,227,229,233,236,239,240,242,244],display_delai:233,display_rgb:27,display_rgb_opt:72,display_skip:233,dist_2d:[69,117],dist_in_met:33,distanc:[2,6,8,10,20,27,32,33,45,63,64,67,69,100,117,121,162,230,240],distance_margin:[63,64],distance_str:162,distant:16,distinct:[22,31,60,69],distinguish:20,distort:[69,231],distortion_:69,distribut:[52,58,66,69,122],disturb:[6,42,64],diverg:69,divers:[44,60],divid:[28,29,31,69],divis:64,divx:242,do_ambiguity_check:[69,100],do_fiducial_loop_closur:69,do_not_download_point_cloud:[69,100],do_odometry_loop_closur:69,doc:[58,62,120,171,172,174,175,176,177,178,179,180,181,182,183,184,185,194,217,233,245],dock:[9,22,68,75,203],dock_config:69,dock_id:[69,94],dock_my_robot:195,dock_properti:69,dock_stat:69,dock_status_dock:69,dock_status_undock:[67,69],dock_status_unknown:69,dock_typ:69,dock_type_contact_prototyp:69,dock_type_spot_dock:69,dock_type_unknown:69,docker:[47,67,222],dockerfil:[49,67,190,221,224,231,242],dockid:195,docking_callback:224,docking_command:94,docking_command_async:94,docking_command_feedback:[67,94],docking_command_feedback_async:94,docking_command_feedback_ful:[67,94],docking_command_feedback_full_async:94,docking_command_ful:[67,94],docking_command_full_async:94,docking_command_id:69,docking_servic:75,docking_station_id:[15,69],dockingcli:[67,94],dockingcomand:15,dockingcommand:[15,69,94],dockingcommandfeedback:[15,69],dockingcommandfeedbackrespons:94,dockingcommandrequest:94,dockingcommandrespons:[67,94],dockingservic:94,dockstat:94,docstr:84,document:[0,3,17,20,21,24,26,28,29,30,31,33,36,37,38,39,45,46,47,48,49,56,57,58,59,61,64,65,66,69,78,135,190,192,196,198,202,203,204,205,206,214,215,216,217,221,224,227,228,229,231,238,242],doe:[6,10,13,17,19,21,24,25,29,31,32,36,37,39,44,45,46,49,55,62,66,67,69,74,85,92,93,97,98,104,107,113,119,122,146,221,225,226,227,231,242],does_dedup_filter_exist:139,doesn:[58,61,63,69,74,100,118],dof:4,dog:[60,61,62,63,64],dogtoi:[60,61,62,63,64],doing:[44,61,63,69,136],domain:97,don:[29,32,56,60,61,62,63,64,65,69,86,117,136,164],done:[18,29,32,41,42,60,61,63,64,69,84,100,119,211,223,227,228],door:[5,6,14,45,68,75,177,196,213],door_command:69,door_command_id:69,door_pb2:95,door_servic:75,doorclient:95,doorservic:[7,67,95,174],doorwai:[7,20],dot:[10,29,31],doubl:[2,16,29,30,31,61,62,69,101,118,125,148,190],doublevalu:[2,69,73],down:[2,7,10,13,21,29,31,32,36,39,42,44,45,50,53,55,61,62,63,64,65,66,69,104,119,123,136,139,171,175,214,217,225,227,228,232,242,245],download:[2,11,15,18,19,22,23,25,26,28,29,30,31,35,44,46,56,60,61,62,63,64,65,67,69,74,75,86,100,171,191,200,214,221,227,231,236],download_bddf:187,download_data:81,download_data_rest:86,download_edge_snapshot:100,download_graph:100,download_graph_async:100,download_imag:[69,100],download_robot_state_log:74,download_started_timestamp:69,download_waypoint_snapshot:100,downloaded_fil:35,downloaded_graph:214,downloadedgesnapshot:[17,18,69],downloadgraph:[17,18,69],downloadrobotstatelog:[2,31],downloadrobotstatelogrespons:74,downloadsnapshot:17,downloadwaypoint:69,downloadwaypointsnapshot:[17,18,69],downloadwaypointsnapshotrequest:67,downsampl:215,dozen:69,drag:[8,29,60,69],drain:227,dramat:[21,32],drastic:18,draw:[6,7,31,62,63,64,67,69,213,233],drawabl:69,drawable_properti:69,drawer:173,drawn:[29,45,63,69,177,213],drift:[24,69,213],drive:[10,14,15,17,28,33,36,43,47,60,62,66,67,69,173,220],driven:[19,33,214],driver:[55,61,67,69,221,227,245],drop:[10,29,45,60,63,64,69,227,242],drop_position_rt_vis:64,dtype:[60,62,63],dual:50,due:[44,58,65,66,67,69,74,97,104,115,213,227,231,233,235],duplic:[49,119,139,148],durabl:50,durat:[2,24,27,28,29,31,32,36,42,53,58,64,66,67,69,74,104,136,162,173,192],duration_sec:74,duration_second:74,duration_str:162,duration_to_second:162,dure:[2,6,9,12,13,16,17,18,19,22,23,24,29,31,35,36,44,45,46,48,53,61,62,65,66,67,69,88,92,93,98,102,104,109,113,115,135,166,171,189,224,236],dust:[8,50],duty_cycl:[2,32],dwell:2,dynam:[0,1,19,20,22,25,31,33,41,44,46,47,49,50,51,55,65,66,67,68,69,71,75,84,104,124,141,142,145,170,187,198,227,236,242],each:[2,4,6,12,13,17,19,20,21,22,24,25,26,27,28,29,30,31,32,33,35,36,37,38,39,40,41,42,45,46,48,52,53,54,58,60,62,63,66,67,69,74,75,84,88,101,102,106,114,119,123,128,148,149,161,163,164,170,172,188,190,192,198,208,211,213,214,215,216,220,227,228,231,233,236,240,242],eap:[55,67],earli:[27,46,67,69,88],earlier:[56,66,67,109],earliest:69,eas:[32,40,228],easi:[17,40,60,61,64,69,123,139,204],easier:[25,38,55,58,69,119,170,222],easiest:20,easili:[29,30,43,46,48,49,50,55,64,65,69,120,213,221],easing_cubic_in_out:2,easing_cubic_input:2,easing_cubic_output:2,easing_exponential_in_out:2,easing_exponential_input:2,easing_exponential_output:2,easing_linear:2,easing_quadratic_in_out:2,easing_quadratic_input:2,easing_quadratic_output:2,easing_unknown:2,ecdsa:67,echo:[44,69],ecryptf:69,edg:[11,13,14,16,18,29,41,54,67,99,100,109,117,213,214,216],edge_env:117,edge_environ:[69,117],edge_id:[69,100],edge_id_list:100,edge_map:69,edge_snapshot:[17,100,213,214,215,216],edge_snapshot_:214,edge_snapshot_id:[69,100],edge_sourc:69,edge_source_alternate_route_find:69,edge_source_fiducial_loop_closur:69,edge_source_odometri:69,edge_source_small_loop_closur:69,edge_source_unknown:69,edge_source_user_request:69,edgeexistserror:117,edgemissingtransformerror:117,edgesnapshot:100,edit:[27,28,29,56,65,162,227],effect:[2,21,29,32,33,48,58,67,69],effector:[6,7,67,69,180,182],effici:[2,25,37,60,69],effort:40,eight:[2,69],either:[2,11,13,17,22,25,29,31,32,33,35,37,40,41,45,46,48,56,62,65,67,69,74,97,100,110,111,117,119,136,148,149,162,205,208,213,214,224,242],el0:[27,69,119],el0_handl:72,el1:[27,69,119],el1_handl:72,elaps:[2,69],elbow0:27,elbow1:27,elbow_0:2,elbow_0_offset:[2,27],elbow_1:2,elbow_1_offset:[2,27],elect:13,electr:[0,15,47,50],element:[2,35,41,67,69,84,148,219],elev:52,elif:[60,62],ellipt:67,els:[29,32,62,63,64,65,67,69,100,245],elsewher:[61,166],email:65,embed:37,emerg:[29,33,65,66,96,231],emiss:[67,69],emit:107,emitt:[103,194],emot:32,empti:[6,19,21,29,31,35,40,42,61,63,66,74,94,99,117,122,136,148],empty_region:117,enab:62,enabl:[10,17,28,29,31,33,37,38,40,45,46,48,53,55,60,62,63,64,65,66,68,69,75,93,115,119,126,129,133,171,172,174,175,176,177,178,179,180,181,182,183,184,185,194,198,205,217,227,228,229,242],enable_bit:69,enable_congestion_control:[69,133],enable_congestion_control_async:133,enable_debug:129,enable_debug_async:129,enable_grated_floor:[67,69],enable_humid:69,enable_shock:69,enable_system_stat:69,enable_temperatur:69,enablecongestioncontrol:69,enabledebug:69,encapsul:[41,69],encod:[2,17,19,24,25,45,67,90,148,242],encoding_paramet:69,encoding_raw:69,encoding_rl:69,encoding_unknown:69,encoding_xyz_32f:69,encoding_xyz_4sc:69,encoding_xyz_5sc:69,encount:[17,18,40,46,67,69,97,166],encourag:[16,45,66,67],encrypt:[44,69],end:[2,6,7,10,15,16,17,22,23,25,27,29,31,32,36,37,44,46,55,56,59,60,63,64,67,69,81,86,90,100,118,119,135,136,143,144,149,160,180,182,192,224,227,231],end_datetim:136,end_nsec:[81,136],end_tim:[2,63,64,69,81,94],end_time_sec:[63,64,86,119],end_timestamp:69,end_timestamp_sec:[90,118],endian:[25,69],ending_veloc:69,endmag:25,endpoint:[8,35,36,39,44,45,49,62,65,67,74,96,100,118,119,136,139,140,164,173,195,214,224,225,228,234,236],endpoint_ip:[225,228],endpoint_port:228,endpointmismatcherror:96,endpointunknownerror:96,endstop:[69,135],energet:29,energi:53,enforc:[29,31,69],engag:[195,204,226],engin:[9,40],enhanc:[45,55],enough:[15,20,21,42,52,58,62,69,104,110,178,184,185,231],enp2s0:55,ensur:[28,29,31,32,40,46,48,50,51,53,54,55,61,65,67,69,102,104,107,113,118,178,184,185,190,213,221,226,227,236,242],ensure_channel:118,ensure_cli:[21,38,40,48,60,62,63,66,67,118,213],ensure_secure_channel:[67,118],enter:[21,29,31,35,56,60,61,62,65,67,69],enterpris:67,entir:[2,6,27,31,32,35,38,42,46,63,66,69,83],entranc:31,entrance_st:2,entri:[22,35,58,62,67,69,83,92,93,113,149],entry_slic:[2,32],enumer:[37,96,104,165],env:117,environ:[6,10,14,17,18,20,21,37,44,45,49,50,55,56,60,61,64,66,67,69,117,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,188,194,213,217,231,233,239,245],environment:[51,188],environmn:69,eof:160,eoferror:160,ephemer:[67,123,139],epoch:[37,42,66,69,104,118,136,148,156,159,162,187,192],equal:[10,22,56,69,213],equip:[20,21],equival:69,eras:[69,100],err_str:62,error:[2,7,17,24,28,31,37,40,45,58,62,65,66,67,74,77,78,79,81,82,84,85,87,88,90,91,92,93,94,96,97,98,99,100,101,102,104,106,107,109,111,113,114,115,117,118,119,122,123,135,136,137,146,164,165,166,167,169,192,225,236,242],error_camera_timeout:69,error_clutch_slip:69,error_cod:123,error_constructor:84,error_data:[69,88],error_edge_id:69,error_endstop_timeout:69,error_existing_edg:69,error_factori:84,error_failed_stand:69,error_fgkc_failur:69,error_from_respons:84,error_ground_check:69,error_init_imu_check:69,error_init_not_sit:69,error_loadcell_timeout:69,error_mass_discrep:69,error_messag:[69,74,78,79,84,85,92,93,96,97,98,100,101,102,104,109,111,113,114,115,117,119,135,164,167],error_msg:[88,123],error_no_result:69,error_non:69,error_obstruct:69,error_pair:84,error_power_off_failur:69,error_power_on_failur:69,error_report:69,error_revert_failur:69,error_unexpected_power_chang:69,error_unknown:69,error_waypoint_id:69,error_waypoint_localized_id:69,error_zero_out_of_rang:69,esc:29,escap:[29,67],especi:[6,32,50,63,69],essenti:[6,64,65,170],essid:69,est:62,establish:[3,20,24,38,45,53,69,82,97,130,136,167,227,237],establish_sess:167,establish_session_async:167,establish_timesync:136,establishsess:[22,69,225],estaimted_payload:69,estim:[7,17,19,21,24,41,45,68,69,118,119,136,162,174,216,240],estimated_end_effector_force_in_hand:69,estimated_payload:69,estimated_runtim:69,estop:[24,39,63,65,66,67,68,75,83,115,118,214,231,238],estop_cli:[66,96],estop_cut_power_timeout:96,estop_endpoint:66,estop_gui:[65,204,214],estop_keep_al:66,estop_level_cut:[66,69,96,204],estop_level_non:[66,69,96],estop_level_settle_then_cut:[69,96],estop_level_unknown:[69,96],estop_nogui:[39,65,204],estop_pb2:96,estop_servic:75,estop_st:69,estop_timeout:[66,96],estopcheckin:69,estopcheckinrequest:39,estopcheckinrespons:39,estopcli:96,estopconfig:96,estopconfigrequest:69,estopendpoint:[66,96],estopkeepal:[66,67,96],estopnogui:67,estoppederror:115,estopresponseerror:96,estopservic:[24,66,67,96],estopst:[45,115],estopsystemstatu:96,etc:[17,25,37,44,49,55,56,65,66,67,69,83,118,130,148,173,216,225],eth0_a_n:53,eth0_a_p:53,eth0_b_n:53,eth0_b_p:53,eth0_c_n:53,eth0_c_p:53,eth0_d_n:53,eth0_d_p:53,ethernet:[3,4,8,15,44,48,53,55,69,84],euler:[2,64,69,110,161],euler_angl:2,eulerzxi:[66,119,161],eval:62,eval_input_read:61,evalu:[60,61,69],even:[2,6,10,14,16,17,27,40,44,45,49,52,63,65,66,67,69,74,130,219,224,236],event:[2,17,19,31,36,53,67,83,88,90,91,118,127,129,192],event_comment_request:69,event_level_min:37,event_typ:[90,118],events_com:69,eventscommentsspec:91,eventu:[64,69,236],ever:[42,46,69,74,113],everi:[17,20,21,22,28,29,32,35,38,43,61,66,69,93,119],everyth:[2,60,62,67,69,88,228],ewrt:69,exact:[28,31,39,41,69],exactli:[2,16,27,29,32,39,64,67,69,104,205,208],exagger:[2,32],examin:84,exampl:[0,2,3,4,6,7,9,10,12,13,14,16,18,19,20,21,24,25,27,28,29,30,35,36,39,41,42,44,45,49,50,55,56,58,59,60,61,63,65,66,67,69,73,88,90,96,100,101,104,109,110,119,169,220,221,222,227,228,231,233,235,236,245],exc:84,exce:[37,53,69,117],exceed:[37,63,66,69,136],excel:[61,65],except:[44,46,58,63,65,66,69,74,75,78,79,84,85,88,90,91,92,93,94,96,98,99,100,101,104,107,109,111,113,114,115,117,118,119,122,135,136,137,146,162,163,164,167,169],exchang:[69,130],excit:64,exclud:69,exclus:[2,27,32,37,69],excut:2,exe:[24,27,28,30,56,65,239],exec:56,execstart:[56,62],execstartpr:56,execstop:[56,62],exectut:69,execut:[2,6,10,13,15,22,26,27,28,29,30,31,40,42,45,49,56,58,65,66,67,69,74,77,84,94,164,169,171,175,176,179,181,183,188,236],execute_choreographi:74,execute_choreography_async:74,executechoreographi:[2,31],executechoreographyrequest:74,executechoreographyrespons:74,executor:88,exercis:[11,19,46,67,234],exhibit:119,exist:[2,6,11,17,24,27,28,29,31,36,39,41,42,44,45,58,60,62,65,67,69,74,85,92,93,96,98,99,100,102,104,113,117,118,122,129,130,139,140,143,160,190,213,214,225,227,238,242],existing_cmd_behavior:69,exists_in_directori:69,exit:[29,46,55,56,60,62,63,64,65,67,69,88,104,123,136,139,220,228,231,233,234,241,242,245],exit_slic:[2,32],exit_st:2,expand:[29,37,38,64],expans:3,expect:[17,21,27,37,38,39,45,46,49,58,65,66,67,69,74,94,99,100,102,104,113,115,148,149,190,215,224,231,232,233,236,245],expected_bits_per_second:69,expens:[2,27,43,58],experi:[29,66,67,69,171,172,174,175,176,177,178,179,180,181,182,183,184,185,194,217,233,242,245],experienc:[50,69,97,100,104],experiment:[65,66,169],expir:[24,44,67,69,78,100,118,122],expiredapplicationtokenerror:78,expirederror:119,expiri:94,explain:[0,19,25,66,67,69],explan:[61,228],explicit:69,explicitli:[2,10,13,15,17,19,28,32,35,55,58,65,67,118,205],explor:[9,44,65,67,69],exporter_main_v2:62,expos:[15,24,45,48,49,50,66,118,119,214,224],exposur:[46,67,69,102],exposure_dur:69,express:[6,13,22,25,28,41,45,63,64,66,69,110,122,161],express_se2_velocity_in_new_fram:99,express_se3_velocity_in_new_fram:99,expressli:42,ext4:69,extend:[2,17,27,29,31,32,36,46,49,52,69],extendable_opt:72,extens:[4,8,12,28,31,56,69,88,89,101],extent:[52,69],extern:[4,19,25,37,40,45,46,48,63,65,67,69,111,118,119,173,190,195,208,232,234,242,245],external_force_ind:[69,119],external_force_non:[69,119],external_force_overrid:69,external_force_param:[69,119],external_force_use_estim:[69,119],external_force_use_overrid:[69,119],external_m:[69,131],externalservererror:111,externalservicenotfounderror:111,extinguish:[190,202,221],extra:[2,6,15,27,44,61,67,69,78,88,112,113,114,126,236],extra_payload:69,extract:[17,25,61,64,67,69,110,187,197,213,215],extract_point_cloud:215,extraordinari:69,extrapol:17,extrem:[29,45,69],extrins:[67,69],f0e835c2:230,fab:63,face:[2,21,32,64,67,69,174,177],facil:66,facilit:69,factor:[69,110],fade:32,fade_color_param:2,fade_in_slic:[2,32],fade_out_slic:[2,32],fail:[2,13,14,22,24,28,31,37,42,44,45,46,49,61,62,63,65,66,67,69,86,95,96,97,100,101,109,111,114,117,118,119,122,135,137,162,164,166,195,213,227,228,229,235,236],failed_nod:69,failed_st:63,failednod:67,failur:[2,22,31,37,40,42,46,65,67,69,88,97,102,104,123,135,165,169,227,236],fake:45,fall:[6,21,31,32,37,50,52,53,64,67,69,171],fallback_log:107,fallen:[33,69,119],fals:[2,22,32,46,48,63,66,69,73,74,81,83,88,90,91,93,96,98,100,102,104,109,115,117,118,119,121,129,135,136,139,148,154,159,160,169,205,213,231],falseclass:[2,69],famili:69,familiar:227,fan:65,faq:30,far:[2,10,12,18,21,32,62,65,67,69,100,117,119,224],farm:36,farther:[32,88],fashion:12,fast:[2,19,31,32,46,69,177],faster:[2,31,32,69,221,227,231,233],faster_rcnn_inception_v2_coco:[221,231,233],faster_rcnn_inception_v2_coco_2018_01_28:231,fastest:[32,55],fastest_ti:32,fault:[3,29,45,46,66,68,69,75,83,93,100,102,115,119,199,201,227,236],fault_client:102,fault_id:69,fault_nam:69,fault_servic:75,faultclient:[98,102],faultcommand:83,faultederror:115,faultresponseerror:98,faultservic:98,faultshowcommand:83,faultstat:[69,115],faultwatchcommand:83,favor:[2,67],fcbwxjy6mmjqg:214,fddb:25,feasibl:[31,69],featur:[6,9,14,17,18,19,20,36,49,55,66,68,69,100,105,170,173,215,216,227],feature_cod:69,feature_en:69,feature_list:105,feature_quality_toler:69,featuredeserterror:100,featureless:[20,69],feed:[67,69],feedback:[15,18,46,58,63,64,66,85,87,95,100,108,119,135],feedback_request:63,feedback_resp:63,feel:[61,69],feet:[2,29,31,32,66,67,69,119,171,234],fell:16,femal:53,fetch:[46,59,67],fetch_serv:64,few:[19,21,24,25,40,44,45,58,60,65,66,67,69,233],fewest:16,ffig:214,fiddl:88,fidget:32,fidget_stand_param:2,fiduci:[6,13,17,19,21,32,41,45,67,69,100,117,195,202,213,214,216,226,240,244],fiducial_001:6,fiducial_filtered_pose_statu:69,fiducial_follow:[67,205],fiducial_id:213,fiducial_init:[69,100],fiducial_init_nearest:[20,69],fiducial_init_nearest_at_target:[20,69],fiducial_init_no_fiduci:[20,69],fiducial_init_specif:[20,69,100],fiducial_init_unknown:69,fiducial_loop_closure_param:69,fiducial_numb:[2,32],fiducial_pose_statu:69,fiducial_pose_uncertain:69,fiducial_posit:213,fiducial_rot:213,fiducial_too_old:69,fiducialinit:20,fiducialposeerror:117,fiduicial_init_specif:67,field:[2,4,14,20,21,27,29,31,35,36,37,39,40,41,42,43,44,45,46,48,49,55,58,62,63,66,67,69,72,73,74,84,89,93,97,100,101,102,109,110,114,116,123,129,162,166,205,224,227,228,242,244],field_desc:169,field_desc_to_pb_typ:169,field_nam:166,field_typ:166,fieldmask:69,fight:213,figur:[32,58,61],figure8:32,figure8_param:2,file:[2,13,17,23,26,28,30,34,36,37,44,48,49,56,58,60,61,62,63,64,65,66,67,69,71,74,83,86,88,89,101,118,142,143,144,145,146,147,148,150,151,152,153,154,155,156,158,159,160,171,173,178,184,186,187,188,189,190,193,195,204,208,209,212,215,217,218,219,221,224,225,226,227,228,230,231,232,233,234,235,236,237,238,239,242,243,244],file_cont:69,file_descriptor:[69,143],file_extens:[69,88,89],file_index:[69,143,148,149],file_lin:66,file_line_split:72,file_nam:[69,74],file_offset:[25,69,149],file_path:74,fileformatdescriptor:25,fileformatvers:143,fileindex:[25,143,148,149],filenam:[27,35,60,67,69,74,81,86,101,137,143,147,188],filepath:[73,101,171,214,224,238,242],filepath_input:73,filesystem:[69,129,137],fill:[2,6,39,40,41,46,62,64,67,69,74,94,100,101,114,121,129,136,224,228,236],filli:214,filter:[37,41,45,63,67,69,139,140,190,212,215],final_iter:69,find:[10,14,24,25,29,60,61,62,63,64,65,67,69,83,97,99,100,101,114,140,166,170,190,213,224],find_center_px:63,findstr:65,fine:28,fine_tune_checkpoint:61,fine_tune_checkpoint_typ:61,finger:[7,8,69],fingertip:[63,69],finish:[15,32,42,60,63,64,69,88,119,213,220,225],finish_block:156,fire:[190,202,221],fire_ext_detector:222,firewal:[44,62,190,221,225,227,228,242],firmwar:39,first:[2,6,21,22,24,25,27,29,31,32,35,37,39,42,44,45,48,49,56,58,60,62,64,65,66,67,69,74,87,96,113,119,121,122,136,187,195,205,213,215,221,228,231,236,242],first_checkin:96,fishey:[41,66,67,69,174,227,231,235],fit:[2,8,29,61,67,69],five:[41,233],fix:[2,19,27,31,32,41,44,46,48,64,69,74,102,173,205,213,242],fixed32:[2,69],fixed64:[2,69],fixnum:[2,69],fl_angles_handl:72,fl_contact:2,fl_contact_handl:72,fl_hx_handler:72,fl_hy_handl:72,fl_kn_handler:72,fl_pos_handl:72,fl_x_handler:72,fl_y_handl:72,fl_z_handler:72,flag:[30,66,67,69,221,242],flashlight:67,flat:[6,20,31,41,69,117,135],flat_bodi:[32,69,110],flat_body_q_hand:64,flat_body_tform_hand:64,flat_ground:[69,117],flatten:[35,110],flatter:58,fledg:65,fleet:[9,23,122],flexibl:[9,25,28],flexion:4,flight:[2,32],flight_slic:[2,32],flip:[2,119],float32:[2,69],float64:[2,69],float_valu:[22,69],floatvalu:[62,63,69],floor:[17,31,32,63,67,69,172,174,175,176,177,178,179,180,181,182,183,184,185,215,217],fluoresc:69,flush:[63,107],fly:60,foam:[50,51],focal:69,focal_length:69,focu:[67,69],focus:[31,46],folder:[27,36,43,60,61,62,63,86,190,213,214,219,224,231,236,245],foldernam:171,follow:[2,3,4,6,7,10,11,13,14,16,17,18,19,20,22,23,25,27,29,30,31,32,33,36,37,39,40,41,42,44,46,48,49,52,55,56,57,58,59,60,61,63,64,65,66,67,69,70,88,90,93,100,110,117,119,121,169,170,177,187,190,196,197,198,199,200,201,202,203,213,219,220,221,222,223,224,225,227,228,232,233,235,236,239,241,242,245],follow_arm_command:119,follow_arm_feedback:69,follow_arm_request:69,followingrouteerror:117,font_hershey_simplex:[62,63],foo:[58,169],fooouterclass:58,fooproto:58,foot:[2,27,28,31,32,45,67,69,119,234],foot_contact_st:2,foot_height_error_from_mean:69,foot_height_result:[67,69],foot_po:[2,27],foot_pos_handl:72,foot_posit:[69,119],foot_position_rt_bodi:69,foot_slip_distance_rt_fram:69,foot_slip_velocity_rt_fram:69,foot_stat:69,footprint:[2,27,32,66,67,69,119],footprint_r_bodi:[66,119],footstat:45,footstep:[2,17],for_autonomous_process:69,forc:[7,8,20,31,50,61,67,69,119,173,182,186,196],force_i:119,force_limit:[69,119],force_remain_near_current_joint_configur:69,force_sigint_captur:[123,139],force_simple_setup:[66,96],force_start_auto_return:186,force_trajectori:175,force_wrench_control:175,force_x:119,force_z:119,forcefulli:[42,69,104],fordur:22,forest:69,forev:[40,60,62,74],forget:[22,56,62,67,69,113],forgotten:[39,113],form:[24,25,32,35,44,58,69,78,90,94,97,99,115,118,162,215,216],format:[17,27,28,29,31,34,35,44,45,46,60,61,62,63,65,66,67,81,85,87,99,101,102,146,147,151,154,155,158,160,162,171,187,190,192,215,221,224,231,233,236,242],format_bddf_fil:69,format_jpeg:[46,62,69],format_metr:162,format_raw:[46,62,63,69],format_rl:69,format_unknown:69,former:65,forth:32,forward:[2,6,32,33,41,44,49,55,56,64,66,67,69,87,100,107,119,185,206,220,224,228],found:[10,17,20,28,31,32,37,41,43,45,52,55,61,62,63,64,65,67,69,92,97,100,101,102,111,114,118,125,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,188,194,217,231,233,242,245],foundat:64,four:[2,15,18,29,31,36,69,171,242],fov:69,fpath:74,fpn:61,fr_angles_handl:72,fr_contact:2,fr_contact_handl:72,fr_hx_handler:72,fr_hy_handl:72,fr_kn_handler:72,fr_pos_handl:72,fr_x_handler:72,fr_y_handl:72,fr_z_handler:72,fraction:[2,27,32,69,110,162],fragil:6,frame1:69,frame1_nam:69,frame2:69,frame2_nam:[69,119],frame2_tform_desired_hand:119,frame:[2,3,17,19,21,25,27,28,31,32,45,50,63,64,69,75,100,101,110,118,119,170,196,203,205,213,215,216,240,244],frame_a:99,frame_b:99,frame_bodi:67,frame_c:99,frame_help:[41,63,64,67,99],frame_id:[2,32],frame_ko:67,frame_nam:[63,64,69,99,119],frame_name_camera:69,frame_name_dock:69,frame_name_draw:69,frame_name_fiduci:69,frame_name_fiducial_filt:69,frame_name_image_coordin:[63,69],frame_name_image_sensor:[63,69],frame_name_local_grid_data:[41,69],frame_name_sensor:69,frame_name_tform_box:69,frame_snapshot_param:2,frame_tform_stair:69,frame_trajectory_command:[67,206],frame_tree_edg:41,frame_tree_snapshot:[99,119],frame_vo:67,frametre:99,frametreesnapshot:[41,67,99,118],frametyp:67,framework:[7,28,31,49],free:[14,24,61,69,228],freedesktop:56,freedom:[4,8,29,69],freez:[2,22,27,29,69,119],freeze_command:119,freeze_feedback:69,freeze_request:69,frequenc:[27,53,115,118,119],frequency_opt:72,frequent:[32,55,69],fresh:[24,138],fri:62,friction:[67,69,117],fridai:37,fridg:69,friend:65,friendli:[24,69,83],from:[0,2,4,6,7,8,10,11,13,14,15,16,18,19,20,21,22,24,25,26,27,28,29,30,31,32,33,35,36,37,38,40,41,42,44,45,46,47,48,49,50,52,53,54,55,56,58,60,61,62,63,64,66,67,69,72,73,74,76,78,81,83,84,85,86,87,88,89,90,91,93,94,95,96,97,98,99,100,101,102,104,106,107,108,109,110,112,113,114,115,117,118,119,120,122,123,129,130,135,136,137,139,140,143,144,147,149,150,151,154,155,157,158,160,162,164,169,170,173,174,177,178,184,187,189,191,192,195,205,208,213,214,215,216,217,219,221,223,224,225,226,227,228,231,232,233,234,235,236,238,242],from_ident:110,from_ko_tform_stair:69,from_matrix:[64,110,213],from_nsec:37,from_obj:[21,64,110],from_pitch:110,from_proto:96,from_rol:110,from_se2:110,from_sec:37,from_t_to:117,from_tform_to:[69,117,213],from_timestamp:69,from_vector:110,from_waypoint:[69,117],from_waypoint_id:117,from_yaw:110,frombuff:[60,62],fromstr:63,front:[2,4,6,15,31,32,41,48,52,60,61,63,64,67,69,119,174,202,208,231,232],front_up_param:2,frontleft:[41,69],frontleft_depth:[66,208],frontleft_depth_in_visual_fram:66,frontleft_fisheye_imag:[60,63,66,208,221],frontright:69,frontright_depth:66,frontright_depth_in_visual_fram:66,frontright_fisheye_imag:[60,63,66],frontup:2,frozen:[66,221],frozen_inference_graph:[221,231,233],full:[2,6,8,17,18,27,31,32,33,36,40,41,44,45,46,53,60,66,67,68,69,73,74,76,83,85,87,90,94,95,100,108,111,118,134,135,156,159,191,208,214,231],full_body_command:119,full_body_feedback:69,fullbodycommand:6,fulli:[6,13,28,31,32,45,46,51,53,54,60,64,67,69,93,115,123,190,205,225,227,228,242],fullstatecommand:83,fun:[64,65],func:84,fundament:170,funk:31,furnitur:20,further:[10,25,27,29,31,50,64,67,69,74,78,120,238],furthermor:67,furthest:69,fut:66,futur:[2,16,24,32,37,55,62,66,67,69,78,84,88,100,119,120],futurewrapp:[77,84],g00:177,g01:177,g02:177,g03:177,g3doc:221,gaffer:177,gain:[42,46,66,67,69,102,125,228],gait:[2,32,69],game:22,gamepad:33,gamma:4,gas:[19,46],gasket:51,gatewai:[48,55,69],gather:[19,48,69],gaug:69,gaussian:69,gaze:[32,67,69,119,196],gaze_center_cfp:[2,32],gaze_mean_period:[2,32],gaze_position_generation_gain:[2,32],gaze_roll_generation_gain:[2,32],gaze_slew_r:[2,32],gaze_to_target_rotation_measur:69,gazecommand:[6,119],gazing_at_target:69,gcc:[65,66],gcode:[6,7,67,196],gcp:[67,224],gener:[2,6,8,11,18,22,24,25,27,29,32,35,36,37,38,41,42,44,46,48,49,58,63,66,67,69,73,74,78,82,85,87,90,92,93,96,98,99,100,101,104,109,110,113,114,115,117,118,119,122,123,135,136,137,139,164,190,216,220,224,227,228,231,233,236,242],generate_animation_id:74,generate_channel_opt:82,generate_client_nam:122,generate_new_anchor:[69,100],generate_route_param:100,generate_tfrecord:61,generate_travel_param:100,generated_id:74,generatetreeerror:99,gentli:[45,64,66],geometr:[21,41,66,69],geometri:[3,17,45,50,52,66,67,68,99,119,141,196,203,206],geometry_pb2:[63,64,110],gestur:32,get:[11,13,14,15,17,18,20,21,22,24,29,31,37,41,45,48,50,59,60,61,62,63,64,66,67,69,74,77,79,83,84,85,86,87,88,92,94,95,99,102,104,105,106,107,110,113,114,117,118,119,120,121,122,125,126,129,130,133,136,140,143,148,149,150,157,164,166,169,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,187,192,194,197,198,199,202,205,213,214,217,228,230,231,233,245],get_a_tform_b:[63,64,67,99],get_audio_capture_channel:125,get_audio_capture_channel_async:125,get_audio_capture_gain:125,get_audio_capture_gain_async:125,get_bit_statu:127,get_bit_status_async:127,get_blob:154,get_blob_spec:[69,91],get_bounding_box_imag:63,get_bytes_field_allowlist:123,get_cached_robot_id:118,get_cached_usernam:118,get_challeng:96,get_closest_se2_transform:110,get_closest_se3_transform:110,get_colormap:230,get_config:96,get_config_async:96,get_configur:79,get_configuration_async:79,get_data_buffer_statu:91,get_data_buffer_status_async:91,get_data_index:91,get_data_index_async:91,get_data_pag:91,get_data_pages_async:91,get_depth_plus_visual_imag:207,get_docking_config:94,get_docking_config_async:94,get_docking_st:94,get_docking_state_async:94,get_entri:92,get_entry_async:92,get_events_com:91,get_events_comments_async:91,get_feature_en:105,get_fiducial_origin:213,get_focu:230,get_frame_nam:99,get_frame_tree_snapshot:[118,119],get_graphnav_origin:21,get_hardware_config_with_link_info:121,get_ice_configur:130,get_ice_configuration_async:130,get_id:[66,118,120],get_id_async:[66,120],get_imag:[46,101,208],get_image_and_timestamp:102,get_image_async:101,get_image_from_sourc:[60,66,101],get_image_from_sources_async:101,get_info:164,get_info_async:164,get_ir_colormap:126,get_ir_colormap_async:126,get_latest_captured_imag:102,get_leas:104,get_lease_st:104,get_led_bright:128,get_led_brightness_async:128,get_license_info:105,get_local_grid:106,get_local_grid_typ:106,get_local_grid_types_async:106,get_local_grids_async:106,get_localization_st:[21,100],get_localization_state_async:100,get_logg:139,get_messag:[150,151,157,158],get_miss:164,get_mission_async:164,get_mission_st:[67,209],get_new_estim:136,get_node_detail:166,get_obj_and_img:[63,64],get_odom_tform_bodi:99,get_payload:67,get_payload_auth_token:113,get_point_cloud:114,get_point_cloud_async:114,get_point_cloud_from_sourc:114,get_point_cloud_from_sources_async:114,get_posit:230,get_power_statu:131,get_power_status_async:131,get_proto_read:[151,152],get_ptz_posit:132,get_ptz_position_async:132,get_ptz_veloc:132,get_ptz_velocity_async:132,get_record_statu:117,get_record_status_async:117,get_request_id:85,get_request_st:88,get_robot_clock_skew:136,get_robot_hardware_configur:121,get_robot_hardware_configuration_async:121,get_robot_joint_model_async:121,get_robot_link_model:121,get_robot_link_model_async:121,get_robot_metr:121,get_robot_metrics_async:121,get_robot_st:[4,64,66,121,210],get_robot_state_async:[66,67,121,211],get_robot_time_convert:136,get_screen:126,get_screen_async:126,get_se2_a_tform_b:99,get_self_ip:[62,67,84],get_service_info:[85,87],get_service_info_async:[85,87],get_software_vers:134,get_software_version_async:134,get_software_version_ful:134,get_software_version_full_async:134,get_stat:164,get_state_async:164,get_statu:[66,85,87,96,129],get_status_async:[85,87,96,129],get_status_proto:88,get_stream_param:133,get_stream_params_async:133,get_temperatur:127,get_temperature_async:127,get_time_sync_upd:136,get_time_sync_update_async:136,get_transl:110,get_value_from_constant_value_messag:169,get_value_from_value_messag:169,get_visible_camera:126,get_visible_cameras_async:126,get_vision_tform_bodi:99,get_volum:[125,230],get_volume_async:125,get_walking_param:[63,64],get_world_object:[67,212],getaudiocapturechannel:69,getaudiocapturegain:69,getauthtoken:[44,69],getauthtokenrequest:[44,67],getauthtokenrespons:[44,67],getbitstatu:69,getconfigur:69,getconfigurationrespons:79,getdatabuffercommentscommand:83,getdatabuffereventscommand:83,getdatabuffereventscommentscommand:83,getdatabufferstatu:69,getdatabufferstatuscommand:83,getdataindex:69,getdatapag:69,getdockingconfig:[15,69],getdockingst:[15,69],getestopconfig:69,getestopsystemstatu:69,geteventscom:69,getfeatureen:[67,69],geticeconfigur:69,getimag:[36,45,46,67,69,102,227,242],getimagecommand:83,getimagerequest:102,getimagerespons:123,getinfo:[22,69],getircolormap:[67,69],getledbright:69,getlicenseinfo:69,getlocalgrid:[45,69],getlocalgridscommand:83,getlocalgridsrespons:123,getlocalgridtyp:[45,69],getlocalizationst:[16,18,21,69],getmapstatu:69,getmiss:[22,69],getobjectlistrespons:69,getpayloadauthtoken:[48,69],getpointcloud:69,getposit:69,getpowerstatu:69,getptzfocu:67,getptzposit:69,getptzveloc:69,getrecordstatu:[18,69],getrobothardwareconfigur:[45,69],getrobotid:[66,69],getrobotlinkmodel:69,getrobotmetr:[45,69],getrobotst:[45,69],getscreen:69,getservic:69,getserviceentri:[48,69],getserviceinfo:[36,46,69,88],getserviceinforequest:88,getserviceinforespons:[85,87,88],getsoftwarevers:69,getsoftwareversionrespons:134,getstat:[22,69],getstatu:[36,46,67,69,86,88,236],getstatusrequest:88,getstatusrespons:[46,88],getstreamparam:69,gettemperatur:69,getvisiblecamera:69,getvolum:69,ghost:29,gif:[2,26,32,64],gigabit:8,gigabyt:37,git:[65,205],github:[0,205,221,231],give:[28,29,31,32,42,48,55,58,66,69,119],given:[13,16,17,18,20,22,32,33,38,41,42,45,52,67,69,82,83,84,90,91,96,100,101,102,104,107,110,114,117,118,136,143,147,149,150,152,154,155,160,162,169,187,228,230],glide:65,glob:[66,69,122],global:[17,19,21,67,69,215,216,242],glossi:20,glove:64,glue:24,gmt:37,gn_origin_tform_bodi:21,gnd:53,gnome:56,gnu:[55,205],goal:[16,29,60,61,63,64,67,69,100,119,205,206],goal_head:[63,64,119],goal_heading_rt_bodi:119,goal_i:[63,64,119],goal_se2:119,goal_waypoint_rt_seed_ewrt_seed_toler:[69,100],goal_x:[63,64,119],goal_x_rt_bodi:119,goal_y_rt_bodi:119,goe:[22,32,59,61,69,227],going:[6,22,29,31,32,61,63,64,66,67,69,107],gokasian:67,gone:[67,69],good:[19,31,40,43,44,60,61,62,63,67,69,104,119,185,213,231],googl:[2,44,55,58,62,63,67,69,70,85,88,90,107,136,162,188],google_application_credenti:188,googleapi:66,got:[21,32,62,63,64,119,221],goto_param:2,gpe:[41,69],gps:69,gps_metadata_plugin_servic:190,gpu:[45,60,61,69,129,221,231],grab:[7,55,69,74,83,104,119,140,164,174],gracefulli:44,grade:69,gradual:32,grai:63,grain:28,grant:[48,104,228],graph:[13,17,18,19,22,24,41,42,61,66,68,75,109,117,170,197,214,215,216,221,226],graph_nav:[20,24,66,75,100,109,117],graph_nav_anchoring_optim:[17,213],graph_nav_cli:[21,213],graph_nav_command_lin:[11,17,67,214],graph_nav_extract_point_cloud:17,graph_nav_servic:75,graph_nav_view_map:67,graphic:[56,61,65,187,235],graphnav:[9,11,12,13,14,44,45,67,69,75,100,109,117,197],graphnavcli:[21,100,213],graphnavrecord:18,graphnavrecordingservic:[12,13,22,67,117],graphnavrecordingservicecli:117,graphnavservic:[12,24,66,67,100],graphnavserviceresponseerror:100,grasp:[6,7,13,63,64,67,69,108,184,196],grasp_don:63,grasp_override_command:108,grasp_override_command_async:108,grasp_override_request:108,grasp_palm_to_fingertip:[63,69],grasp_param:[63,69],grasp_params_frame_nam:[63,69],grasp_planning_solut:69,grasp_position_constraint_fixed_at_user_posit:69,grasp_position_constraint_norm:69,grasp_position_constraint_unknown:69,grasp_request:63,graspabl:63,grass:69,grate:[67,69,117],grated_floor:[67,69,117],grated_surfaces_mod:[67,69],grated_surfaces_mode_auto:69,grated_surfaces_mode_off:69,grated_surfaces_mode_on:69,grated_surfaces_mode_unknown:69,grav_aligned_body_frame_nam:64,gravel:69,graviti:[2,41,64,66,69,99,110,119],gravity_ewrt_se:69,grayscal:[45,62,63,67],great:[29,60],greater:[2,27,56,69,231,233],greatest:25,green:[2,27,63,65,69,204,213,216,227],grei:[29,31],grep:65,greyscal:[45,69],grid:[41,67,68,69,75,83,240],gripper:[2,6,7,28,29,31,42,63,64,66,68,69,177,196,223],gripper_angl:2,gripper_command:[6,64,119],gripper_command_feedback:69,gripper_handl:72,gripper_multipli:[2,27],gripper_nearest_object:69,gripper_offset:[2,27],gripper_open_percentag:69,gripper_param:2,gripper_q:119,gripper_strength_fract:[2,27],grippercommand:6,grossli:61,ground:[2,7,20,29,32,41,45,53,60,67,69,117,135,171,182],ground_contact_normal_rt_fram:69,ground_mu_est:69,ground_mu_hint:[67,69,117],group:[2,27,29,30,35,40,69,85,86,170],group_nam:[35,36,69,85,86],group_name_format:69,grow:67,grpc:[24,25,31,37,46,57,58,62,65,66,67,69,70,77,81,82,84,88,93,102,104,112,113,114,115,118,123,136,137,139,142,145,198,225,227,228,231,233,236,242],grpc_messag:66,grpc_proto_read:150,grpc_python_out:228,grpc_reader:[151,152],grpc_servic:[37,81],grpc_service_read:152,grpc_service_writ:153,grpc_statu:66,grpc_tool:228,grpcio:228,grpcprotoread:[150,151,152],grpcreader:151,grpcrequest:145,grpcrespons:145,grpcserieswrit:153,grpcserviceread:152,grpcservicerunn:[38,123,139],grpcservicewrit:153,guarante:[6,24,39,41,45,69,74],guard:64,guarente:69,guess:[13,20,24,69,100,109,213],gui:[2,33,48,61,65,67,231],gui_requir:187,guid:[0,26,32,40,46,48,49,57,58,59,60,61,65,66,67,69,98,113,118,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,190,194,201,205,214,217,223,227,228,229,242],guidanc:50,guidelin:[17,47,48,52],gxp:44,gyro:69,gyroscope_weight:69,gzip:69,had:[17,64,67,69,88,100,119],half:[52,69],halfwai:63,hallwai:[20,21,213],halt:6,hamburg:[62,64,225],hammi:214,hand:[2,7,8,27,28,32,60,63,64,67,69,103,185,194,232],hand_distance_to_goal_measur:69,hand_euler_rpi:27,hand_euler_rpy_angles_handl:72,hand_ewrt_flat_bodi:64,hand_pitch_handl:72,hand_po:27,hand_pos:2,hand_pos_handl:72,hand_position_at_go:69,hand_quat_w_handl:72,hand_quat_wxyz:27,hand_quat_x_handl:72,hand_quat_xyzw:27,hand_quat_y_handl:72,hand_quat_z_handl:72,hand_quaternion_wxyz_handl:72,hand_quaternion_xyzw_handl:72,hand_roll_at_go:69,hand_roll_handl:72,hand_roll_to_target_roll_measur:69,hand_x_handl:72,hand_y_handl:72,hand_yaw_handl:72,hand_z_handl:72,handi:55,handl:[7,25,29,37,38,45,46,52,53,56,58,61,69,84,88,104,112,114,115,118,119,122,137,174,221,228],handle_common_header_error:84,handle_lease_use_result_error:84,handle_nested_double_value_param:73,handle_respons:84,handle_response_stream:84,handle_stal:69,handle_stale_fail:69,handle_stale_read_anywai:69,handle_stale_run_until_fresh:69,handle_stale_unknown:69,handle_typ:69,handle_type_fixed_grasp:69,handle_type_knob:69,handle_type_lev:69,handle_type_unknown:69,handle_unset_status_error:84,handler:[69,84,107,123,139],handlestal:67,handoff:44,handshak:44,handwritten:67,happen:[17,28,39,44,59,61,66,67,69,93,100,101,104,114,171],happend:69,happi:63,hard:[2,6,27,50,64,69,188,195],hardcod:[48,69],hardwar:[16,24,40,45,46,50,53,66,67,69,83,119,121,190,210,220,233,236],hardware_configur:69,hardwareconfigur:[67,121],hardwareconfigurationcommand:83,has:[0,2,4,6,7,8,10,13,14,15,16,17,18,19,21,22,24,25,26,27,28,29,31,32,33,37,39,40,41,42,43,44,45,46,48,50,55,56,60,61,62,63,64,65,66,67,69,74,78,88,93,96,100,101,102,104,107,109,114,115,117,118,119,121,135,136,144,146,169,177,187,192,204,205,213,221,223,226,227,228,231,236,242,245],has_arm:[67,74,118,121],has_data:69,has_data_error:88,has_established_time_sync:136,has_remote_point_cloud_sensor:69,hash:[2,25,27,69,74,149],hasn:63,have:[2,5,6,7,14,16,17,19,20,21,22,24,25,27,28,29,31,32,33,35,37,38,39,40,41,43,44,45,46,48,51,52,55,58,60,61,62,63,64,65,66,67,69,74,88,94,97,102,104,107,110,119,123,136,140,154,160,172,173,185,186,187,188,189,195,205,206,212,213,214,215,216,217,220,224,225,226,227,228,230,231,232,234,236,237,238,239,242,243,244],haven:[69,156],head:[2,6,32,60,61,62,63,119],header:[2,25,37,44,46,58,62,66,67,68,84,97,116,123,135,144,148],header_pb2:[62,123],heading_rt_vis:[63,64],headless:61,health:[0,29,45,48,68,124,129,135],health_scor:69,healthclient:127,healthservic:[67,127],hear:[10,164],heart:88,heartbeat:[39,40,69,93],heavi:[6,47,50,52,69],height:[2,4,8,17,20,32,45,60,66,67,69,110,119,213,216,240,242,245],height_z:110,held:[6,27,42,69,118],hello:[67,170,198,225],hello_spot:[60,65,66,217],hello_world_mission_servic:225,hellospotclientlaptop:66,helloworldservic:225,help:[11,13,15,17,18,19,20,24,25,29,31,33,39,42,43,46,50,55,59,60,61,62,63,65,66,67,69,94,96,119,136,171,172,174,175,176,177,178,179,180,181,182,183,184,185,187,190,194,213,217,227,233,236,245],helper:[31,38,41,46,63,64,66,69,71,73,74,75,84,85,88,93,94,101,113,114,117,118,119,123,139,173,190,225,227,228,236,242],henc:[50,66],here:[2,22,25,29,31,38,40,43,45,52,55,60,61,62,63,65,66,69,88,119,170,172,173,174,175,176,177,178,179,180,181,182,183,184,185,188,217,221,224,227,231,233,242],hhmmss:192,hide:[44,104],hierarch:69,hierarchi:[13,22,31,42,67,69],high:[2,3,6,7,15,16,22,24,26,28,29,31,32,37,44,45,53,62,66,67,69,213,215,227],higher:[2,3,7,17,24,27,32,42,44,45,49,50,67,69,96,216,231,242],highest:[42,63,65,69,231],highest_conf:63,highli:[29,65],highlight:[31,170],hind:[2,4,32],hinder:67,hing:[7,67,69,174],hinge_sid:69,hinge_side_left:69,hinge_side_right:69,hinge_side_unknown:69,hint:[17,65,67,69,109,117,119,213],hint_ambl:69,hint_auto:69,hint_auto_ambl:69,hint_auto_trot:69,hint_crawl:69,hint_hop:69,hint_jog:69,hint_left:69,hint_right:69,hint_speed_select_ambl:69,hint_speed_select_crawl:69,hint_speed_select_trot:69,hint_trot:69,hint_unknown:69,hint_weight:69,hip:[2,41,52,67,69],hip_i:[2,32],hip_range_of_motion_result:69,hip_x:[2,32],histor:[45,67,69],histori:69,historical_fault:69,history_lower_tick_bound:69,history_past_tick:69,history_upper_tick_bound:69,hit:[29,33,69],hl_angles_handl:72,hl_contact:2,hl_contact_handl:72,hl_hx_handler:72,hl_hy_handl:72,hl_kn_handler:72,hl_pos_handl:72,hl_x_handler:72,hl_y_handl:72,hl_z_handler:72,hntr5:54,hold:[2,6,8,32,37,61,64,65,67,69,83,119,205,233],hold_zero_ax:[2,32],holder:[66,69,104],holding_toi:63,hole:69,home:[22,56,60,61,65],hop:[2,69,245],hop_param:2,hope:64,hopefulli:10,horizon:[69,221],horizont:[2,31,32,41,43,63,67,126,215],host:[0,22,24,38,39,44,48,49,55,60,62,67,69,81,82,93,118,123,139,190,221,222,224,225,227,228,229,231,242],host_computer_ip:[49,190,224,227,242],host_ip:[40,69,93],host_nam:104,host_payload_guid:69,hostcomputeripcommand:83,hostnam:[29,30,33,37,49,60,62,63,65,69,81,86,139,189,190,191,213,221,222,225,227,231,233,242,245],hotkei:[29,33],hour:[24,37,48,67,187,190,192,228],house_plan:213,hover:29,how:[2,7,11,13,17,19,21,22,24,27,29,30,31,32,34,36,38,39,40,41,43,44,45,46,49,55,56,58,59,60,61,62,63,64,65,67,69,88,94,100,119,122,135,143,148,149,170,172,173,175,178,180,182,184,187,188,189,190,191,192,193,194,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,217,219,223,224,225,226,227,228,229,230,231,235,237,238,239,240,242,243],howev:[6,8,9,15,17,19,20,21,24,29,30,31,33,41,42,43,44,46,48,50,55,58,65,67,69,88,99,104,113,190,205,208,213,228,242],hr_angles_handl:72,hr_contact:2,hr_contact_handl:72,hr_hx_handler:72,hr_hy_handl:72,hr_kn_handler:72,hr_pos_handl:72,hr_x_handler:72,hr_y_handl:72,hr_z_handler:72,html:69,http2:69,http:[24,37,49,55,56,61,65,66,67,69,86,148,205,221,224,227,231],huge:61,human:[8,20,27,28,31,36,42,45,46,69,83,90,117,118,126,228,231],humid:[69,129],hundr:[17,67],hunk:90,hxa:69,hya:69,hybrid:[6,69,175],icd:0,ice:230,ice_serv:130,ice_set:230,icon:[2,56,227],icp:[69,100],icp_it:69,icp_param:69,icp_vari:69,id_client:66,id_end:69,id_service_nam:118,id_start:69,id_str:[90,118],idea:[7,45,67],ideal:[58,84],ident:[6,24,32,56,69,94,100,110,208,213,236],identif:[69,96,221],identifi:[2,24,31,35,36,40,42,43,45,46,63,66,67,69,87,88,89,94,100,118,122,136,146,149,228,244],identifier_hash:69,identifit:69,identify_object:221,identify_object_without_robot:221,idl:32,idr:69,idr_interv:133,idrinterv:69,ids:[69,86,100,117,214],iff:69,ignor:[2,6,27,32,39,40,64,67,69,119,126],ignore_final_yaw:69,ignore_is_valid_check:104,ignore_resourc:104,ikqi6hgq:214,ill:[69,97],illumin:67,illustr:[41,50],imag:[2,3,4,7,17,20,22,28,29,31,33,35,36,38,41,42,43,55,60,61,62,63,64,67,68,75,83,85,87,88,89,100,111,129,133,174,177,178,184,189,190,198,199,202,205,213,217,221,222,224,230,231,233,241],image_captur:[60,69,88],image_cli:[60,66],image_data:[46,102],image_decod:[46,102],image_decode_with_error_check:102,image_dir:61,image_format:[46,69,101,102],image_fram:102,image_ful:63,image_height:62,image_nam:[49,102],image_param:69,image_pb2:[46,62,63,101,102,123],image_properti:[62,63,69,221],image_proto:[46,101,102],image_request:[69,101],image_respons:[60,63,66,69,101],image_rotation_angl:69,image_saved_path:60,image_servic:[75,102],image_service_help:[46,102],image_service_pb2_grpc:[38,102],image_service_test:[46,236],image_sourc:[60,63,69,101,102],image_source_and_servic:[63,69],image_source_nam:[69,101,102],image_tensor:62,image_typ:[69,102],image_type_depth:69,image_type_unknown:69,image_type_visu:69,image_uuid:230,image_view:208,image_width:62,imageacquistioncap:46,imagecaptur:[88,89,101],imagecapturethread:102,imagecli:[60,66,101],imagecommand:83,imagedataerror:101,imagedemo:188,imageformat:46,imagerequest:101,imagerespons:[101,123,235],imageresponseerror:101,imageri:[17,216],imageservic:[24,36,46,66,67,101,236],imageserviceservic:102,imagesourc:[67,101,102,236],imagesourceandservic:63,imagetyp:[67,102],imagin:14,imdecod:[60,62,63],img:[60,63],immedi:[7,28,31,32,33,39,46,52,53,66,67,69,88,96,118,225],immut:69,impact:[44,45,50,52,69],impair:[67,69],impaired_st:69,impaired_statu:69,impaired_status_behavior_fault:69,impaired_status_no_motor_pow:69,impaired_status_no_robot_data:69,impaired_status_ok:69,impaired_status_remote_clouds_not_work:69,impaired_status_service_fault:69,impaired_status_system_fault:69,impaired_status_unknown:69,impared_statu:69,imped:69,imperfect:63,impl:[22,69,166],impl_typenam:69,implement:[0,3,9,11,18,19,22,24,39,40,43,44,45,46,58,66,67,69,79,85,87,88,89,96,102,107,169,199,225,228,242],impli:[25,66,67,69],implic:44,implicit:69,imposs:[21,69],improperli:[44,69],improv:[17,37,40,43,44,45,65,69],imshow:63,imu:[8,69,135],imwrit:[60,62],inabl:44,inaccess:10,inaccessibleparametererror:166,inaccur:[19,213],inact:[62,69,227],inactivethreaderror:136,inadvert:[6,67],incandesc:69,incept:231,inch:177,inclin:69,includ:[0,2,4,5,7,8,9,11,12,14,17,19,21,22,24,25,27,28,29,32,35,36,37,38,40,41,42,44,45,46,47,48,49,50,52,53,55,58,60,61,62,63,65,66,67,69,70,74,78,82,84,85,86,87,88,95,100,101,104,108,109,117,119,121,136,139,140,144,170,173,188,190,192,208,216,221,223,227,231,233],include_dedup_filt:139,include_each_leg:32,include_front_left_leg:2,include_front_right_leg:2,include_full_lease_info:[69,104],include_hind_left_leg:2,include_hind_right_leg:2,include_statu:169,inclus:69,inclusion_exclud:2,inclusion_if_st:2,inclusion_includ:2,inclusion_unknown:2,incom:[42,62,69,74,88,104,119],incoming_lease_proto:104,incompat:[31,65],incompatible_hardware_error:69,incomplet:100,incompletedata:74,inconsist:[20,74,213],incorpor:[6,51,67,170],incorrect:[2,29,39,62,65,67,69,74,85,87,96,146,195,228,242],incorrect_challenge_respons:39,incorrectchallengeresponseerror:96,incorrectli:[36,58,67,69,78,88,118],increas:[44,52,63,65,66,67,69,104,231,233],increment:[32,42,69],increment_slic:[2,32],inde:225,indefinit:69,indent:58,independ:[12,15,30,32,40,44,66,69,97],independent_color_param:2,index:[15,25,62,67,68,69,90,91,142,143,147,148,150,151,154,155,157,158,160,192,242],index_data_block:149,index_in_seri:[147,150,151,154,155,157,158],index_offset:144,indic:[2,13,15,16,24,27,28,29,31,32,40,42,44,46,64,66,67,69,74,78,86,88,100,102,104,117,118,119,128,129,139,164,192,220,221,227],individu:[6,17,27,31,35,37,39,42,46,67,69,213,242],indoor:50,industri:[19,50],ineffici:[2,69],inert:47,inerti:[41,69],inertia:69,inf:63,infer:[2,20,27,67,69,221],infil:[143,147],influenc:[45,69],info:[2,18,37,61,69,83,88,115,120,139,192,213,224,225,242],inforamt:69,inform:[0,2,3,4,15,17,19,20,21,22,24,25,27,29,31,32,35,36,37,38,40,42,43,44,45,46,47,48,51,55,56,57,58,59,65,66,67,69,73,85,87,88,92,93,100,102,104,110,117,118,119,122,123,134,136,147,164,170,171,188,190,192,193,198,204,209,213,223,225,227,228,229,236,242,244],infrastructur:[44,58],ing:[49,231],ingress:[4,8,51],inherit:[38,46,67,84,102],init:62,init_wrench_direct:69,init_wrench_direction_in_frame_nam:[69,119],initi:[2,9,13,17,18,21,24,29,31,32,36,40,41,45,58,60,61,66,67,69,74,100,102,109,117,122,132,136,164,172,175,176,178,179,180,181,182,183,184,208,213,214,217,224,226,227],initial_guess:[20,69,100],initial_guess_loc:100,initial_hint:[69,109,213],initialize_fault:102,initialize_len:[131,132],initialize_lens_async:132,initialize_payload:228,initializelen:[67,69],inject:65,inkscap:177,inner:[22,50],innermost:69,inoper:6,input:[6,7,19,43,44,46,49,53,58,61,62,63,67,69,73,74,88,99,104,110,119,136,143,162,167,174,190,214,221,227,242],input_data:[62,63,69],input_map:213,input_path:61,input_tensor:62,input_typ:62,inrush:53,ins:[45,66,96,104],insecur:82,insert:[22,25,29,37,55,69,115,220],insid:[6,13,35,45,52,65,69,110,213,221,228],inspect:[17,19,35,69,88,115],instal:[22,49,53,55,59,60,61,67,69,83,105,134,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,189,190,191,192,193,194,195,204,205,207,208,209,210,211,212,213,214,215,216,217,218,219,220,223,225,226,228,229,230,232,234,236,237,238,239,240,241,242,243,244],install_d:[66,69],instanc:[2,17,37,38,40,45,46,65,67,69,74,76,82,84,88,93,94,95,98,102,104,108,118,119,122,136,228],instantan:69,instanti:[118,169],instantli:29,instead:[2,10,21,24,25,27,29,32,40,46,48,49,55,61,62,64,67,69,80,107,119,131,188,208,221,222,225,227],instruct:[49,55,56,65,190,205,220,224,227,231,233,242],insuffici:[44,69],int32:[2,31,62,63,69],int32valu:[2,69],int64:[2,25,69],int64valu:69,int8:69,int_valu:69,integ:[2,25,27,31,42,46,69,96,101,148,162,231],integr:[3,8,28,34,36,43,47,52,55,57,62,236],intellig:119,intend:[23,32,37,69,87,88,96,107,119,205,220,227],intens:[45,69,235,240],intent:[67,131],intention:215,intenum:[96,165],interact:[7,8,22,24,44,47,49,55,56,65,66,67,69,83,104,130,139,205,230,240],intercept:66,interconnect:50,interest:[6,36,62,69,101,173],interf:55,interfac:[0,15,17,24,36,37,43,44,46,47,49,50,55,58,59,66,67,69,71,75,81,83,84,88,102,119,124,137,141,142,163,187,204,215,216,220,227,241],interfer:[8,67],interlock:[15,53],interlockmotor:53,intermedi:[44,69],intermediari:130,intermitt:69,intern:[2,4,20,21,25,40,44,45,58,67,69,88,96,101,102,104,107,114,135,136,213],internal_server_error:58,internalservererror:[97,104],internet:[3,44,55,224],interp:110,interpol:[17,28,69],interpret:[7,24,27,43,44,65,66,67,69,73,148,169,177],interrupt:[16,44,53,69,195,228],intersect:[20,69],intertia:69,interv:[13,17,29,67,69,77,93,118,136],interven:21,intervent:6,intranet:3,intrins:[67,69,101],intro:28,introduc:[8,17,69],introduct:[6,170],introductori:[67,180,217],invalid:[2,16,24,28,44,45,67,69,74,78,79,85,96,97,99,100,101,104,109,114,119,135,156,164,169],invalid_hint:69,invalid_param:69,invalid_request:58,invalid_schema_id:69,invalidanswercod:164,invalidapplicationtokenerror:78,invalidapptokenerror:97,invalidargu:[90,91,107],invalidclientcertificateerror:97,invalidconvers:169,invalidedgeerror:100,invalidendpointerror:96,invalidgrapherror:[100,109],invalidhintserror:109,invalididerror:96,invalidleaseerror:104,invalidloginerror:[44,65,78,118],invalidparametererror:79,invalidparamserror:109,invalidpayloadcredentialserror:[113,118],invalidposeerror:100,invalidquestionid:164,invalidrequest:2,invalidrequesterror:[67,97,100,118,119],invalidresourceerror:104,invalidsessionid:167,invalidtokenerror:[78,118],invaliduploadedchoreographyerror:74,invers:[41,69,110],invert:[41,69],invoc:[112,113,114],invok:[83,104],involv:[17,53,69,100,219,225],ioerror:122,ion:53,ip54:[4,50,51],ip_where_plugin_will_run:190,iperf3:189,ipv4:[55,66,69],ir_enable_dis:103,ir_enable_disable_servic:75,ircolormap:126,irenabledis:[67,69],irenabledisableservic:[67,103],irenabledisableservicecli:[103,194],is_al:[93,104,113,138],is_author:[48,69],is_battery_low:22,is_battery_low_miss:22,is_block:84,is_cancel:88,is_download:69,is_en:[48,69],is_estop:[67,96,118],is_extend:2,is_gravity_aligned_frame_nam:99,is_gripper_holding_item:69,is_lost:69,is_metadata:[69,148,159],is_noncompute_payload:[48,69],is_open:69,is_point_cloud_process:69,is_powered_on:[66,115,118],is_record:69,is_robot_following_hand:69,is_string_identifi:169,is_thread_al:107,is_tim:69,is_valid_leas:104,is_valid_proto:104,is_within_threshold:110,isdir:62,isinst:62,isn:[2,15,21,24,61,62,64,69],iso8601:69,isrecordingerror:100,issu:[2,6,7,14,15,17,22,24,30,40,42,44,45,48,55,58,61,62,63,65,66,69,74,76,78,94,95,96,100,101,102,104,108,111,114,115,118,119,135,174,190,206,210,214,227,228,231,236,242,244],issue_acquire_data_request:86,item:[6,7,45,48,61,62,67,69,178,184],iter:[13,22,63,69,84,88,109,136,157,205,213,231,233],its:[2,6,7,13,14,15,16,17,18,19,20,21,22,24,25,26,28,29,31,32,33,35,36,37,38,39,41,45,46,48,49,50,52,58,60,62,63,64,65,66,67,69,88,94,100,104,116,119,150,172,178,184,185,205,206,208,213,221,225,227,228,232,234,235],itself:[3,6,16,18,24,35,37,38,39,41,44,45,48,50,55,62,66,67,69,93,104,113,118,225,228],jacobian:69,jan:[65,66],java:[2,58,69],java_outer_classnam:58,jaw:[8,69],jet:[69,230],jitter:67,jog:[67,245],join:[44,60,62,69,102],joint:[2,27,28,31,32,41,45,64,67,69,119,135,171,196],joint_angl:2,joint_limit:69,joint_stat:[4,69],jointspac:[2,27],journalctl:55,joystick:[6,69],jpeg:[45,46,62,67,69,101,102,208,230],jpg:[35,60,61,62,66,101,230],json:[36,46,69,85,87,188,190,230],jump:[2,31,55,63],jump_param:2,just:[6,7,15,22,28,29,30,61,63,64,65,66,67,69,74,84,117,122,189,208,213,214,227],jwt:[24,122],kaleidoscop:28,kbrandes01:66,kcapabl:46,keep:[2,6,8,10,19,21,28,29,31,38,40,42,45,46,49,52,58,61,62,63,64,65,66,67,69,93,96,97,104,113,135,149,190,224,231,242],keep_al:[38,40],keep_running_cb:[66,96,104],keepal:[40,66,104],keepalivestatu:96,kei:[0,2,24,25,27,28,29,31,33,44,59,60,61,62,66,67,69,73,74,84,85,87,89,90,91,94,100,107,115,130,137,140,143,145,146,148,149,156,164,220,224,225,245],kelvin:69,kept:[15,122,139,228],kera:[61,221,222],kernel:55,key_fram:2,key_to_series_identifier_hash:69,keyboard:[55,65,67,228],keyerror:160,keyfram:[2,26,28,67],keypoint_orb:69,keypoint_simpl:69,keypoint_unknown:69,keypress:33,keystrok:[29,33],kick:[88,93,113,118,225],kill:[56,66,228],kimagesourc:[63,64],kind:[17,25,46,67,69,148,149,154,191],kinemat:[22,28,41,45,67,69,119],kinematic_cal_result:69,kinematic_odometry_weight:69,kinematic_st:[64,69],kinematicst:[45,67],kitchen:69,kna:69,knee:[2,4,20,31,32,41,52],kneel:[2,31],kneel_circle_param:2,kneel_clap:32,kneel_leg_move2_param:2,kneel_leg_move_param:2,kneellegmov:2,kneellegmove2:2,knob:[28,69,173],knock:177,knonerror:88,knot:69,know:[2,17,41,45,48,66,67,69,111,119,213,242],knowledg:[24,41,45,206],known:[17,18,19,20,24,29,31,41,42,45,46,48,69,74,99,100,102,104,118,162,214,216,227],ko_tform_bodi:[69,100],kserviceauthor:62,kw_arg:[112,113,114],kwarg:[44,66,74,76,78,79,84,85,87,89,90,91,92,93,94,95,96,98,100,101,103,104,105,106,107,108,109,111,114,115,117,119,120,121,125,126,127,128,129,130,131,132,133,134,135,136,139,140,164,167,168],lab:20,label:[17,22,29,60,61,62,63,64,67,69,216,221,228],label_map:[61,62,63,64],label_map_path:61,label_map_util:62,label_path:62,label_prefix:[48,69],labelimg:[60,61],labelprefix:67,labels_fil:62,labels_path:61,lack:[21,231],lag:69,lah:61,laid:[40,228],lamport:104,lan:48,land:[2,32,55],landing_extent_i:69,landing_extent_x:69,languag:[0,44,58,66,74],laptop1:35,laptop:[3,30,43,49,225,228,231],larg:[6,14,17,18,19,21,28,32,37,40,43,44,45,60,66,67,69,100,123,177,213,214],larger:[2,17,45,52,67,69,97,233],laser:46,laser_scan_dens:46,laser_scan_spars:46,last:[2,14,28,31,32,33,42,45,61,63,64,65,66,69,74,102,104,109,117,136,187,231,233,242,245],last_cal_timestamp:69,last_command:69,last_ko_tform_go:69,last_upd:[69,93],lastli:[46,62,69,190,227,236,242],latch:[7,69],late:2,latenc:[6,46,67,69,231,233],later:[9,22,24,32,35,36,37,44,46,55,61,62,67,69,88,107,191,228],latest:[2,19,22,30,42,49,61,65,67,69,74,77,102,118,224,227],latest_known_leas:69,latest_resourc:69,latter:65,launch:[56,65,67,138,171,172,174,175,176,177,178,179,180,181,182,183,184,185,206,217,221,227,238,242],launcher:65,laundri:64,layer:[3,19,24,28,44,45,50,60,69],layout:[17,33,67,69,214],lbs:4,ld_library_path:205,lead:[19,55],lead_auto:2,lead_front:2,lead_hind:2,lead_left:2,lead_leg_pair:[2,32],lead_right:2,lead_unknown:2,leaf:[42,69],lean:69,learn:[11,12,60,61,62,63,64,67,69,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,194,197,198,199,202,217,227,233,245],leas:[2,3,16,33,45,63,64,68,74,75,79,83,84,94,95,97,100,115,117,119,135,164,167,173,186,213,214,220,225,241,245],lease_cli:[63,64,66,104],lease_curr:104,lease_keep_al:66,lease_own:[66,69,104],lease_pb2:[74,104],lease_proto:104,lease_resourc:69,lease_servic:75,lease_st:104,lease_statu:104,lease_use_result:[2,69,104],lease_wallet:[104,213],leasea:42,leaseb:42,leasecli:[63,66,104,213],leasecommand:83,leaseerror:74,leasekeepal:[63,66,104,213],leaselistcommand:83,leasenotownedbywallet:[67,104],leaseresourc:104,leaseresponseerror:104,leaseretain:69,leaseservic:[66,104],leasest:104,leaseuseerror:[95,97,100,104],leaseuseresult:[2,42,67,104],leasewallet:[94,104],leasewalletrequestprocessor:104,leasewalletresponseprocessor:104,least:[2,17,21,25,27,29,32,45,46,53,62,69,172,178,184,220,228,236],leav:[10,20,40,65,69,88,94,234],led:[3,31,67,68,128],led_light_left1:2,led_light_left2:2,led_light_left3:2,led_light_left4:2,led_light_right1:2,led_light_right2:2,led_light_right3:2,led_light_right4:2,led_light_unknown:2,left:[2,4,6,29,32,33,35,40,41,49,54,60,66,69,74,119,208,213,216,220,227,232,240,242,245],left_camera_tform_soccer_bal:41,left_color:[2,32],left_depth:66,left_depth_in_visual_fram:66,left_fisheye_imag:[60,63,66],left_fisheye_image_0000:[60,61],left_fisheye_image_0001:61,left_hip_i:[2,32],left_hip_x:[2,32],left_kne:[2,32],left_x:62,leg:[4,28,29,31,32,41,42,69],leg_angles_handl:72,leg_front_left:2,leg_front_right:2,leg_gesture_mean_period:[2,32],leg_hind_left:2,leg_hind_right:2,leg_joint:27,leg_no_leg:[2,32],leg_pair_distance_chang:69,leg_pair_result:[67,69],leg_unknown:2,len:[41,61,62,63,69],length:[2,4,17,31,45,58,64,67,69,82,122,123,139,162],less:[17,22,29,31,32,39,52,64,67,69,88,115],let:[2,3,6,7,16,29,56,57,58,60,61,62,63,64,66,67,69,130,213],letter:[20,55,214,227],levata:67,level:[3,6,7,17,20,22,26,31,32,37,39,44,45,58,60,65,66,67,90,96,107,118,139,242],level_debug:69,level_error:69,level_high:69,level_info:69,level_low:69,level_medium:69,level_mission_crit:69,level_system_crit:69,level_unknown:69,level_unset:69,level_warn:69,lever:[67,69],lexicograph:69,lgpl:55,lhs:[22,69],lib:[62,66,205],librari:[0,9,24,31,38,39,42,44,49,58,63,65,66,67,69,70,74],licens:[26,30,55,65,66,68,75,83,97,100,117,122],license_servic:75,license_statu:69,licensecli:[67,105],licensecommand:83,licensed_featur:69,licenseerror:97,licenseservic:[67,105],lidar:[17,19,20,21,46,48,55,67,69,117,215],lie:[41,69,117],lies:52,life:[59,67,69],lifecycl:88,lift:[2,6,8,32],liftoff:[2,27,31,32],liftoff_veloc:32,light:[3,4,20,29,31,47,60,67,69,103,124,194,203,230],light_sid:[2,32],light_side_both_in_sequ:2,light_side_both_match:2,light_side_left:2,light_side_right:2,light_side_unknown:2,lighter:29,lightingcli:128,lightingservic:[67,128],like:[2,4,10,12,15,17,27,28,29,31,32,35,40,41,45,48,52,55,58,60,61,62,63,64,65,66,67,69,74,84,85,87,89,90,91,94,97,100,107,115,140,147,148,162,164,190,208,214,216,223,225,236,242],likelihood:[6,44],likewis:[9,37],limb:[2,69],limit:[4,8,10,18,24,27,31,37,39,44,48,52,53,64,67,81,97,113,173,189,205,228],lin_i:110,lin_x:110,lin_z:110,linalg:64,line:[10,17,23,24,27,29,30,31,33,37,43,46,54,56,58,61,62,63,64,65,66,69,72,73,81,96,177,178,187,188,192,197,205,206,208,210,213,215,216,220,225,227,228,229,233,235,236,240,242],line_numb:69,linear:[6,22,64,67,69,110],linear_spe:69,linear_veloc:69,linestrip:69,link:[6,10,29,32,33,38,39,44,45,46,48,49,55,61,82,102,121,213],link_model:69,link_nam:[69,121],link_status_connect:69,link_status_detect:[67,69],link_status_error:69,link_status_unknown:69,link_to_next:[2,32],lint:88,linux:[29,30,55,65,66,188,190,205,239,242],list:[2,6,15,16,18,22,24,25,28,29,31,35,36,41,42,43,45,48,49,55,56,62,65,67,69,74,77,79,82,83,85,87,88,89,90,91,92,94,99,100,101,102,104,105,106,110,111,112,114,118,119,122,125,126,127,129,132,140,145,148,149,160,166,167,169,171,178,184,186,187,189,190,191,192,193,195,204,208,209,212,214,217,218,219,220,223,225,226,227,228,230,231,232,233,234,236,237,238,239,241,242,243,244],list_all_mov:74,list_all_moves_async:74,list_async:92,list_available_models_command:111,list_available_models_command_async:111,list_available_models_status_external_server_error:69,list_available_models_status_external_service_not_found:69,list_available_models_status_success:69,list_available_models_status_unknown:69,list_camera:[129,230],list_cameras_async:129,list_capture_act:89,list_capture_actions_async:89,list_image_sourc:[66,101],list_image_sources_async:101,list_leas:[66,104],list_leases_async:104,list_leases_ful:104,list_leases_full_async:104,list_logpoint:[129,230],list_payload:[48,112],list_payloads_async:112,list_physical_devic:61,list_point_cloud_sourc:114,list_point_cloud_sources_async:114,list_ptz:132,list_ptz_async:132,list_request:111,list_screen:126,list_screens_async:126,list_servic:118,list_sound:125,list_sounds_async:125,list_stored_data:89,list_stored_data_async:89,list_stored_imag:89,list_stored_images_act:89,list_stored_images_async:89,list_stored_metadata:89,list_stored_metadata_async:89,list_world_object:140,list_world_objects_async:140,listallmov:[2,31,74],listallmovesrespons:31,listavailablemodel:[43,62,69],listavailablemodelsrequest:[62,111],listavailablemodelsrespons:[62,111],listcamera:69,listcaptureact:69,listdata:69,listen:[44,56,69,190,225,227,242],listimagesourc:[36,45,46,69,102,242],listimagesourcescommand:83,listimagesourcesrequest:102,listleas:[67,69],listleaseresourc:69,listleasesrespons:104,listlocalgridtypescommand:83,listlogpoint:69,listpayload:[48,69],listpointcloudsourc:69,listptz:69,listscreen:69,listserviceentri:[24,48,69],listsound:69,liststoreddata:69,liststoredimag:69,liststoredmetadata:69,listworldobject:[45,67,69],lit:[20,227],lithium:53,littl:[25,37,55,62,69],live:[24,38,39,40,45,60,61,67,69,93,104,208,213,227,229,233,240,242],live_data:69,live_index:69,live_keypoint:69,liveness_timeout_sec:[69,93],load:[2,4,13,20,22,27,28,33,49,61,62,64,67,69,74,122,147,164,214,215,221,230,238],load_app_token:122,load_cell_result:69,load_choreography_sequence_from_binary_fil:74,load_choreography_sequence_from_txt_fil:74,load_graph_and_snapshot:213,load_miss:164,load_mission_as_chunk:164,load_mission_async:164,load_robot_cert:122,load_sound:125,load_weight:61,loadcel:[69,135],loaded_edge_snapshot_id:69,loaded_waypoint_snapshot_id:69,loadmiss:[22,69],loadmissionaschunk:69,loadmissionrequest:67,loadsound:69,local:[2,9,11,12,13,16,17,18,35,55,56,67,68,74,75,83,90,100,107,117,118,136,162,188,190,205,208,213,214,215,216,222,224,225,227,240,242],local_grid:[83,106],local_grid_pb2:123,local_grid_request:69,local_grid_respons:69,local_grid_servic:75,local_grid_typ:69,local_grid_type_nam:[69,106],local_ip:49,local_time_nsec:162,local_time_sec:[136,162],local_timestamp_proto:162,localauthor:56,localgrid:41,localgridcli:106,localgridcommand:83,localgridresponseproto:106,localgridservic:[67,106],localhost:[22,56,61,225],localization_request:69,localization_varnam:69,locat:[2,7,10,13,14,15,16,17,18,19,20,21,22,25,27,29,31,32,33,38,41,44,45,48,54,55,60,61,62,63,64,66,67,69,73,74,119,144,149,185,190,205,213,214,220,226,227,231,234,240,242,244],lock:[24,44,67,69,78],locomot:[6,9,11,17,18,32,48,69,119],locomotion_charge_percentag:[22,69],locomotion_estimated_runtim:69,locomotion_hint:[69,119],locomotionhint:67,log:[2,23,25,33,48,49,55,56,60,62,65,66,68,74,75,77,81,83,85,87,88,90,93,102,113,118,122,123,124,139,153,170,171,187,192,224,225,230,233],log_annot:107,log_annotation_pb2:123,log_annotation_servic:75,log_client:107,log_ev:[90,118],log_preserve_hint:[69,90,118],log_preserve_hint_norm:69,log_preserve_hint_preserv:69,log_preserve_hint_unset:69,log_request:153,log_respons:153,log_spot_data:218,log_token_time_remain:122,log_typ:[2,74],log_type_last_choreographi:2,log_type_manu:2,log_type_unknown:2,logannotationcli:[67,107],logannotationhandl:107,logannotationservic:[67,107],logannotationtextmessag:107,logdir:61,logger:[38,74,77,84,85,87,88,89,90,91,93,94,96,100,102,107,113,115,123,139,140,164],logic:[25,31,37,48,53,69,104],login:[48,55,67,69,78,224],logpoint:[67,129,230],logpreservehint:[67,90],logtick:69,logtyp:74,longer:[16,20,21,29,31,32,37,42,44,45,46,58,62,63,64,66,67,69,88,104,115,119,122,136,169,171],look:[4,6,21,27,31,32,43,53,55,60,61,62,64,66,69,92,171,173,211,213,217,224,225,227,242],lookup:[44,69,100,107],lookuperror:90,loop:[2,13,22,27,50,60,62,63,64,67,69,109,224,225],loop_closure_weight:69,loop_count:69,loos:[53,69,110],lose:10,loss:[10,37,61,66,67,69,104,195,225],lossi:[45,69],lost:[16,17,20,28,40,42,67,69,100],lost_detector_st:69,lot:[21,45,60,61,62,64,66,69,119],love:31,lover:64,low:[10,13,17,20,22,29,37,44,52,53,66,67,69,100,173,231],lower:[3,15,17,32,44,66,67,69,110,213,216,220,227,231],lower_limit:110,lower_mid_left:[2,32],lower_mid_right:[2,32],lower_tick_bound:164,lowercas:58,lowermost:69,lowest:[55,69],lux:4,lvi:61,mac:[69,205,235],mac_address:69,machin:[17,30,39,50,56,60,62,63,65,67,69,189,202,215,216,222],maco:[59,65,66,67,231,233,245],macroblock:69,made:[2,24,31,36,60,62,65,66,67,69,88,100,101,104,107,118,162,213],magic:25,magnitud:69,mai:[2,6,10,13,14,17,19,20,21,22,23,24,25,27,29,30,31,32,36,37,38,40,41,44,45,46,48,50,52,55,56,58,61,65,66,67,69,84,93,96,97,99,104,106,109,118,119,131,139,148,154,170,171,189,190,192,213,214,216,219,220,221,225,226,227,230,231,242],mail:52,main:[2,10,27,29,32,42,49,55,60,62,63,67,69,73,81,83,85,111,118,188,204,231,233,242],mainli:[29,69],maintain:[6,16,17,21,24,32,40,44,45,50,51,65,66,69,93,122,136,139,228,229],major:[44,66,69],major_vers:[66,69],make:[2,6,10,16,17,18,19,22,25,29,32,38,40,45,46,49,51,55,58,60,61,62,63,64,65,66,67,69,109,169,171,172,174,175,176,177,178,179,180,181,182,183,184,185,188,195,198,205,206,208,213,217,220,225,227,228],make_acquire_data_request:85,make_add_world_object_req:140,make_add_world_object_request:41,make_capture_paramet:102,make_change_world_object_req:140,make_data_descriptor:149,make_delete_world_object_req:140,make_edg:117,make_edge_environ:117,make_error:[46,88],make_image_sourc:102,make_recording_environ:117,make_subleas:67,make_time_query_param:86,make_time_query_params_from_group_nam:86,make_waypoint_environ:117,male:53,malform:[69,118,213],malfunct:69,malici:48,man:[2,29,32,62],manag:[8,24,29,33,38,40,42,46,47,48,50,56,69,75,77,88,104,135,136,137],maneuver:52,mani:[2,6,19,24,27,29,30,31,32,33,40,41,43,44,45,47,60,61,65,66,67,69,100,107,118,177,213,221,236],manip:69,manip_state_attempting_raycast:69,manip_state_don:69,manip_state_grasp_fail:[63,69],manip_state_grasp_failed_to_raycast_into_map:[63,69],manip_state_grasp_planning_no_solut:[63,69],manip_state_grasp_planning_succeed:69,manip_state_grasp_planning_waiting_data_at_edg:[63,69],manip_state_grasp_succeed:[63,69],manip_state_grasping_object:69,manip_state_moving_to_grasp:69,manip_state_moving_to_plac:69,manip_state_place_fail:69,manip_state_place_failed_to_raycast_into_map:69,manip_state_place_succeed:69,manip_state_placing_object:69,manip_state_searching_for_grasp:69,manip_state_unknown:69,manip_state_walking_to_object:69,manipul:[5,6,44,47,60,62,68,69,75,119,184,196,213],manipulation_api_cli:[63,108],manipulation_api_command:[63,108],manipulation_api_command_async:108,manipulation_api_feedback_command:[63,108],manipulation_api_feedback_command_async:108,manipulation_api_feedback_request:[63,108],manipulation_api_pb2:[63,108],manipulation_api_request:[63,108],manipulation_api_servic:75,manipulation_camera_sourc:69,manipulation_camera_source_hand:69,manipulation_camera_source_stereo:69,manipulation_camera_source_unknown:69,manipulation_cmd_id:[63,69],manipulationapi:[67,69,108],manipulationapicli:[63,108,178,184],manipulationapifeedback:69,manipulationapifeedbackrequest:[63,108],manipulationapifeedbackrespons:108,manipulationapirequest:[63,108],manipulationapirespons:108,manipulationapiservic:[7,108,174],manipulationfeedbackst:[63,67],manipulator_st:[67,69],manner:[19,32,35,65,67,242],manual:[2,15,17,18,19,28,29,30,31,48,49,61,67,69,74,100,174,177,186,214,224,227,228],manual_focu:230,map:[9,11,13,14,16,18,20,21,23,25,29,33,35,41,44,45,46,61,68,73,75,83,84,100,107,117,118,126,143,154,170,197,213,214,215,240,242,245],map_directori:226,map_on_server_was_modifi:69,map_pb2:213,map_process:109,map_processing_cli:213,map_processing_pb2:213,map_processing_servic:75,map_stat:69,map_state_ok:69,map_state_too_large_for_licens:69,map_state_unknown:69,mapmodifiederror:109,mapnavig:11,mapprocessingservic:109,mapprocessingservicecli:[109,213],mapprocessingserviceresponseerror:109,maptoolargelicenseerror:[100,117],mar:[31,61],margin:[69,100,205],mark:[2,14,27,29,46,48,67,69,88,244],mark_request_cancel:88,mark_request_finish:88,marker:[2,31,205,220],mask:69,masquerad:48,mass:[2,8,27,32,52,67,69],mass_volume_properti:69,massless:49,master:[65,69,221],mat:[64,110],match:[2,16,20,28,29,31,35,40,41,49,58,67,73,74,89,93,96,98,113,117,122,137,139,146,148,149,166,221,225,227,228,240],mate:53,materi:[24,50,60,69],math:[63,64,66,67,75],math_help:[21,41,63,64,99,110],mathemat:41,mathworld:69,matplotlib:213,matric:110,matrix:[6,41,64,67,110,148,213],matter:[63,64,119],max:[2,4,6,8,10,31,67,69,73,82,84,97,115,117,118,122,123,128,135,139,164,231,233],max_accel:119,max_angl:69,max_angular_vel:119,max_angular_veloc:69,max_attempt:69,max_chunk_s:125,max_displac:69,max_dist:[20,69,100],max_dur:119,max_fiducial_dist:69,max_gaze_pitch:[2,32],max_i:[63,69],max_it:69,max_linear_vel:[64,119],max_linear_veloc:69,max_loop_closure_edge_length:69,max_loop_closure_height_chang:69,max_loop_closure_path_length:69,max_message_length:122,max_move_length_slic:2,max_point_match_dist:69,max_pos_tracking_error:69,max_receive_message_length:[82,123,139],max_rot_tracking_error:69,max_rotation_vel:64,max_sampl:136,max_send_message_length:[82,123,139],max_start:69,max_status_queue_s:[67,96],max_temp:126,max_tim:2,max_time_second:69,max_translation_met:110,max_vel:[64,69,119],max_vel_linear:64,max_vel_se2:64,max_work:[62,123],max_x:[63,69],max_yaw:[20,69,100],max_yaw_degre:110,max_z:69,maxim:17,maxima:69,maximum:[2,4,6,10,16,21,27,28,29,31,32,36,52,53,67,69,109,126,136,205],maximum_acceler:69,maximum_open_close_acceler:69,maximum_open_close_veloc:69,maximum_paramet:2,maximum_torqu:69,maximum_valu:27,maximum_veloc:69,maxiterationserror:109,maxlen:169,maxtimeerror:109,maya:[28,67],mayb:[65,74],maybe_rais:84,mdn:44,mean:[2,8,16,22,25,31,32,39,48,54,58,61,62,64,67,69,74,115,148,192,205,227,233],meant:[25,46,69,74,96,189],measur:[17,24,25,27,32,45,61,67,69,127,213],measured_pos_distance_to_go:69,measured_pos_tracking_error:69,measured_rot_distance_to_go:69,measured_rot_tracking_error:69,measurement_param:69,measurement_tim:69,mech:[69,230],mechan:[0,7,16,37,44,45,47,69],media:[69,124,230],media_log:[129,230],medialog:129,medialogcli:129,medialogservic:[67,129],medium:[37,47,69],meet:[4,20,63,215],member:[2,35,67,69,96,118],memori:[2,61,69,129,137],mention:[41,69,74],menu:[6,10,28,29,33,43,62,64,225,227,242],merg:[67,69,117],mesh:83,meshlab:215,messag:[2,4,5,6,7,17,19,21,22,24,25,27,28,31,36,39,40,41,44,45,46,58,61,62,67,69,72,73,74,76,81,82,83,84,85,86,87,88,90,91,92,95,97,98,100,101,102,104,107,108,110,112,113,114,116,117,118,119,122,123,136,139,140,142,145,147,148,149,150,151,152,153,157,158,159,164,166,187,191,192,218,228,236,242],message_read:[154,158],message_typ:[69,81,145,148,149,154],messagechannel:145,messageoverrideerror:166,messageread:[154,158],messagetypedescriptor:[148,149],messg:69,messsagetypedescriptor:69,met:[39,58],metadata:[2,24,25,31,36,67,82,83,85,86,87,88,89,148,190],metadata_channel:69,metadata_nam:69,metadata_to_proto:85,metal:[67,69,117],meter:[2,6,8,10,17,20,27,29,32,33,41,60,64,67,69,100,117,119,162,174,177,178,184,205,206,213,231],method:[2,20,24,25,27,29,36,38,44,49,55,63,65,66,67,69,77,82,88,89,93,102,104,107,113,118,122,139,147,151,154,155,158,191,218,225,227],metric:[17,45,55,61,67,69,83,109,121,162,210],metricscommand:83,metronom:29,mh1:53,mh2:53,michigan:20,microphon:[67,69,125],microsecond:69,microservic:66,microsoft:30,mid:69,middl:[6,10,41,52,69,213,216,240],midwai:32,might:[2,17,20,22,25,27,40,43,48,49,50,52,58,60,62,63,65,66,69,221],millidegre:69,millisecond:208,mimic:33,min:[2,31,62,63,67,69,73,117],min_angl:69,min_confid:[62,63,69],min_gaze_pitch:[2,32],min_i:63,min_loop_closure_path_length:69,min_move_length_slic:2,min_temp:126,min_tim:2,min_timeout:69,min_vel:69,min_x:63,mind:[29,40],minim:[6,10,24,25,40,50,55,66,67,69,119],minima:69,minimum:[2,27,29,31,32,36,60,63,69,126],minimum_paramet:2,minimum_valu:27,minor:69,minor_vers:69,mint:[44,69,78,138],minut:[2,4,24,27,28,29,31,32,61,67,69,187,190,192,233],mirror:[2,32,69],mirror_i:32,mirror_x:32,misalign:69,mismatch:[100,166],miss:[2,62,67,69,99,100,109,117,166,167],missing_fiduci:69,missing_input:69,missing_lease_resourc:69,missing_snapshot_id:69,missing_waypoint_id:69,missingfiducialserror:117,missinginput:167,missingleas:167,missingparametererror:166,missingsnapshotserror:109,mission:[9,12,13,14,17,19,20,23,35,36,37,38,42,48,65,68,70,164,165,166,167,168,169,170,188,189,198,224],mission_fil:226,mission_id:69,mission_info:69,mission_record:[67,220],mission_servic:163,missioncli:[163,164],missionresponseerror:164,missionservic:[12,67,164],mistak:64,misumi:54,misus:99,mitig:44,mitm:44,mix:[6,29,69,74],mjpg:242,mkdir:[60,61,62],ml_servic:[63,64],mnt:66,mobil:[5,24,42,52,64,66,68,69,119,196],mobilenet:231,mobility_command:[6,22,67],mobility_command_feedback:69,mobility_command_pb2:22,mobility_feedback:[63,69],mobility_param:[64,67,69,119],mobilitycommand:[6,22],mobilityparam:[67,119],mod:69,mode:[2,6,7,13,32,33,36,46,60,61,67,119,175,177,205,214,220,227,241,245],mode_access_point:69,mode_cli:69,mode_unknown:69,model:[3,17,45,51,52,55,57,60,63,64,67,69,83,111,121,170,213,215,221,222,231,233],model_dir:[61,62,221],model_directori:221,model_main_tf2:61,model_nam:[62,63,69,221],model_path:62,modem:44,moder:31,modif:[29,61,65,67,69,242],modifi:[6,9,10,30,32,45,46,52,56,66,67,69,88,93,109,139],modify_anchoring_on_serv:[69,109,213],modify_map_on_serv:[69,109],modul:[58,59,65,69,88,104,119,231,242],modulenotfounderror:65,moi:69,moi_tensor:69,mojav:[59,65],mold:51,moment:69,monitor:[19,38,40,45,55,61,67,69,77,86,88,115,190,220],monoton:[53,104],month:190,more:[2,5,6,9,10,12,17,19,21,24,25,27,29,31,32,33,35,37,38,39,41,44,45,46,48,51,52,54,55,56,60,61,62,64,65,66,67,69,78,79,84,100,102,104,109,110,113,115,117,119,170,173,175,188,190,197,198,199,201,206,208,213,220,227,228,229,233,236,240,242],mortum:69,most:[2,24,28,31,32,37,40,41,42,45,46,49,55,61,62,65,66,67,69,74,84,104,113,114,122,126,139,170,213,228,242],most_restrictive_travel_param:169,mostli:[62,69],motion:[2,6,24,27,28,31,32,52,67,69,74,119,171,177,196,206],motion_is_absolut:[2,32],motor:[22,29,33,39,45,58,65,67,69,96,115,171,172,174,175,176,177,178,179,180,181,182,183,184,185,204,205,217,220,232,234,241,245],motor_power_st:69,motor_power_state_error:69,motor_power_state_off:69,motor_power_state_on:69,motor_power_state_powering_off:69,motor_power_state_powering_on:69,motor_power_state_unknown:69,motorsonerror:96,mount:[19,43,47,49,51,52,53,67,69,213,227],mount_frame_body_payload:69,mount_frame_gripper_payload:69,mount_frame_nam:69,mount_frame_unknown:69,mount_frame_wr1:69,mount_tform_payload:69,mountframenam:67,mous:[29,55,216,240],move2:32,move:[2,7,10,13,14,17,19,20,22,24,26,27,28,30,31,33,41,45,50,60,62,63,64,69,73,74,109,119,171,172,173,178,180,182,184,185,195,196,205,206,220,225,226,245],move_cmd:[63,64],move_length_slic:2,move_length_tim:2,move_param_config:2,moveinfo:[31,73],movement:[2,32,67,69,119],moveparam:31,moveparamsconfig:2,mp_safety_in:53,mp_safety_o:53,mplbackend:213,mscoco_label_map:64,msg:[90,107,166,169],msg_age_limit:107,msg_num_limit:107,msg_valu:69,msgtype:69,mtu:69,much:[2,7,19,29,31,32,45,60,61,66,67,69,172,206],mult:110,multi:[17,56,69],multipl:[2,15,17,19,20,24,27,30,31,32,33,35,36,40,41,42,44,45,46,49,62,66,67,69,74,102,104,110,119,154,190,205,213,216,223,228,236,242],multiplex:44,multipli:[2,27,41,64,69,125],music:[2,26,30,31,33],music_fil:2,music_start_slic:2,must:[2,10,13,16,17,19,20,21,22,24,27,28,29,30,31,32,33,37,38,39,40,44,45,46,48,50,51,52,53,55,58,61,66,67,69,82,84,86,88,93,99,104,117,119,129,140,148,164,172,174,175,176,177,179,180,181,182,183,185,188,190,191,192,194,205,207,208,210,211,213,214,220,221,223,224,225,227,228,229,236,240,242],mutabl:69,mutat:[41,45,46,73,102,104,116,123,140,202,244],mutate_world_object:[41,67,140,243],mutate_world_objects_async:140,mutated_object_id:69,mutateworldobject:[45,69],mutateworldobjectrequest:140,mutation_req:[41,140],mutual:[2,27,32,37],my_anim:[27,28],my_estop:66,my_graph_fold:214,my_metadata_channel:35,my_password:55,my_spot_env:[60,61,62,65],my_ssd_resnet50_v1_fpn:[61,62],my_wifi:55,myriad:38,myvari:22,name:[2,4,17,22,24,25,28,29,31,35,36,37,38,40,41,42,44,45,46,48,49,53,55,56,58,60,61,62,63,64,65,66,67,69,73,74,77,78,81,83,84,85,86,90,92,93,96,97,98,99,101,102,104,106,107,113,114,116,117,118,119,121,122,126,129,137,143,145,146,148,149,151,152,154,159,166,169,188,190,191,205,208,213,216,221,222,223,224,225,227,228,236,242],name_prefix:69,named_arm_position_command:69,named_arm_position_feedback:69,namedarmposit:6,namedarmpositionscommand:119,namedwindow:63,namespac:[25,58,69],nano:66,nanosecond:[37,136,162,187,192],narg:62,narrow:21,nat:130,natur:[67,69,235],nav:[17,22,24,42,66,68,75,117,170,197,215,226],nav_pb2:100,naviagatetoanchor:67,navig:[9,10,14,17,19,20,21,22,23,29,33,45,69,100,213,214,219,224,226,227,235],navigate_rout:100,navigate_route_async:100,navigate_route_ful:[67,100],navigate_route_full_async:100,navigate_route_response_blackboard_kei:69,navigate_to:100,navigate_to_anchor:100,navigate_to_anchor_async:100,navigate_to_async:100,navigate_to_ful:[67,100],navigate_to_full_async:100,navigate_to_response_blackboard_kei:69,navigaterout:[14,18,67,69],navigaterouterequest:67,navigaterouterespons:[67,100],navigateto:[14,17,18,67,69],navigatetoanchor:[17,69],navigatetorespons:100,navigation_feedback:100,navigation_feedback_async:100,navigation_feedback_response_blackboard_kei:69,navigationfeedback:[16,18,69],navigationfeedbackrespons:[16,100],navigationfeedbackstatu:67,nbsp:[48,75],nbyte:149,ncb_client:221,ncb_server_cpu:221,nearbi:[10,14,45,67,69,100,220],nearest:[17,20,45,67,69,242],nearli:[2,24],nearmap:69,neccessari:69,necessari:[10,17,38,42,44,46,49,51,55,56,63,66,67,69,73,82,100,104,117,147,164,190,205,224,228,232],necessarili:[13,32,69,242],necessesarili:69,need:[2,10,13,15,16,17,19,20,24,25,27,29,30,35,38,40,41,42,43,44,45,46,48,49,56,58,59,60,61,62,63,65,66,67,69,78,88,89,97,104,113,118,131,136,138,147,148,149,159,164,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,193,195,208,212,213,217,220,224,225,226,227,228,229,230,231,232,233,234,237,238,239,242,243,244,245],need_authent:83,needn:2,neg:[2,21,31,45,53,63,67,69,109],negat:64,neighborhood:69,neither:[24,29],nest:[22,35,44],net:4,netmask:[48,69],network:[3,9,24,38,43,45,47,49,56,57,58,60,61,62,63,66,68,75,122,124,126,129,189,198,202,222,224,227,230,231,233,236],network_compute_bridg:43,network_compute_bridge_cli:[63,111],network_compute_bridge_command:[63,111],network_compute_bridge_command_async:111,network_compute_bridge_pb2:[62,63],network_compute_bridge_servic:75,network_compute_bridge_service_pb2_grpc:62,network_compute_cli:[63,64],network_compute_request:111,network_compute_serv:[62,63,64],network_compute_server_output:62,network_compute_status_external_server_error:69,network_compute_status_external_service_not_found:69,network_compute_status_rotation_error:69,network_compute_status_success:69,network_compute_status_unknown:69,network_delai:233,networkcli:130,networkcomput:[62,69],networkcomputebridg:111,networkcomputebridgecli:[63,111],networkcomputebridgeservic:111,networkcomputebridgework:[62,190],networkcomputebridgeworkerservic:[62,111],networkcomputeinputdata:63,networkcomputerequest:[63,111],networkcomputerespons:[62,111],networkcomputerotationerror:111,networkcomputeserverconfigur:63,networkmanag:[55,56],networkservic:[67,130],neural:221,neutral:[2,27,32],neutral_start:[2,27],neutral_start_opt:72,never:[8,24,25,29,44,53,58,69,118,206],new_cmd_behavior:69,new_endpoint:[39,69],new_subgraph:69,newaxi:62,newer:[42,104,245],newest:[42,69,104],newli:[17,48,69,118,122,213,223],newton:69,next:[2,14,15,20,21,29,31,55,60,61,62,63,64,67,69,74,102,160,227,231,233,245],nicknam:[24,65,66,67,69],nmcli:55,nnnnnnnnnn:[187,192],nnnnnnnnnnnnnnnnnnnn:[187,192],no_debug:62,no_loop:[2,27],no_looping_opt:72,noanchoringerror:100,nodata:35,node:[13,41,67,68,99,164,166,169,220],node_impl:166,node_nam:166,node_proto:166,node_refer:69,node_spec:169,node_spec_to_short_str:169,node_st:69,nodes_pb2:[22,169],nodest:13,nodeunreferenceableerror:166,nogui:65,nois:[40,69],nomin:[2,27,32,45,67,69,119],nomissionerror:164,nomissionplayingerror:164,non:[2,20,22,32,36,41,42,44,47,61,66,67,69,104,115,118,119,122,131,136,137,204,221,226],non_strict_pars:[2,31,74],none:[2,31,39,44,46,63,64,66,69,74,77,78,79,81,82,83,84,85,86,87,88,89,90,92,93,94,96,97,98,99,100,101,102,104,107,109,110,111,113,114,115,117,118,119,121,122,123,125,129,131,133,135,136,138,139,140,143,147,148,154,156,157,159,160,162,164,166,167,169,235],nonetyp:104,nonexist:97,nonexistentauthorityerror:[67,97],nonexistentserviceerror:92,nonlinear:69,nonsens:31,nonzero:139,nopatherror:100,nor:[24,29,65],norecordedinform:74,norm:64,normal:[2,10,15,31,32,41,45,52,53,62,69,110,173,213,225],nosuchleas:[67,104],not_carri:67,not_hold:69,not_manag:104,not_valid_aft:69,not_valid_befor:69,notabl:[37,45],notactiveleaseerror:104,notat:41,notauthoritativeserviceerror:104,notclearederror:119,note:[0,2,10,17,20,22,24,27,29,30,32,33,35,37,38,42,45,46,49,52,53,55,59,60,62,63,64,65,66,69,72,74,88,102,117,118,130,169,173,189,190,205,206,208,213,214,215,220,221,224,225,227,228,229,232,235,236,242],notestablishederror:[118,136],notfounderror:[67,97],noth:[29,32,61,63,67,69,73,74,107,117,119,122,123,139],notic:[17,55,63],notimesyncerror:[100,119,140],notimplementederror:107,notincacheerror:[118,137],notlocalizedtoenderror:117,notlocalizedtoexistingmaperror:117,notpoweredonerror:119,notreadyyeterror:117,notrecordingerror:117,notset:107,now:[14,17,20,22,29,30,36,39,41,42,48,55,56,60,61,62,63,64,65,66,67,69,73,79,84,101,119,122,156,187,192,213,225,245],now_nsec:162,now_sec:162,now_timestamp:162,nowow:69,nsec:[148,156,159],nsec_to_sec:162,nsec_to_timestamp:162,ntf:69,ntp:24,nudg:29,num:61,num_cells_i:69,num_cells_x:69,num_class:61,num_com:69,num_data_block:[147,155],num_data_buffer_pag:69,num_detect:62,num_ev:69,num_extra_loop_closure_iter:69,num_local_grid_error:69,num_messag:[150,157],num_object:62,num_point:69,num_retri:94,num_speed_ti:[2,32],num_tick:69,num_train_step:61,number:[2,10,16,17,20,22,24,25,27,29,31,32,37,39,42,44,46,56,60,61,62,63,64,65,66,67,69,86,93,94,99,100,102,104,107,109,113,117,118,119,121,123,136,139,147,150,155,157,162,190,213,220,225,227,231,233,242],number_of_circl:[2,32],numer:[29,31,37,67,69,84,221],numpi:[60,62,63,67,70,110,215,216],nut:54,nvidia:[61,221,231],nx3:110,obei:[58,69],obj1_label_person:221,obj:[62,63,69,121,213],obj_label:63,obj_model:69,object:[2,6,7,21,28,37,38,41,46,58,60,61,62,63,64,67,68,69,73,74,75,76,77,81,83,84,85,86,87,88,89,90,91,93,94,95,96,100,102,104,107,108,109,110,113,115,116,118,119,120,122,123,136,137,138,139,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,159,161,162,164,168,169,170,188,196,198,205,213,221,227,228,231],object_anchor:[69,213],object_detect:[61,62,64,221],object_hint:213,object_in_imag:[62,63,69],object_rt_fram:69,object_typ:[69,74,140],objmodel:121,obs:33,observ:[6,8,19,24,37,48,69,213,220],obsolet:67,obstacl:[6,14,16,17,18,33,45,67,69,117,205,240],obstacle_avoidance_pad:69,obstacle_param:69,obstacleparam:67,obstruct:69,obtain:[66,69,90,100,101,102,114,121,136,164,221,231,233],obviou:[32,40],obvious:[64,67],occasion:[44,46,52,67,88],occassion:69,occupi:69,occur:[6,17,32,40,45,46,53,67,69,74,88,97,117,118,121,135,166,171,236],oct:65,octob:37,ocu01:69,ocu:69,ocur:69,odd:69,odom:[6,19,41,67,69,99,110,119,206],odom_frame_nam:67,odometri:[17,21,41,67,69,119,213],odometry_loop_closure_param:69,off:[2,10,11,15,22,28,29,31,32,33,37,38,39,40,43,45,48,60,63,64,65,67,69,88,93,104,113,115,118,131,171,190,195,204,205,214,221,225,227,232,234,242,245],offer:[3,8,37,44,45,48,49,62,69,130],offici:[61,245],offlin:97,offload:221,offset:[2,22,24,25,27,32,45,64,67,69,100,149,205,206],offset_dist:69,offset_slic:[2,27],often:[2,6,39,41,45,60,61,62,64,66,69,88,90,97,135,227,244],oil:19,okai:[61,213],old:[14,19,25,37,44,67,69,78,82,93,227],old_offset:69,old_zero:69,older:[42,67,69,88,104,230],older_than_tim:88,oldest:[37,69],omit:[10,69],on_failure_callback:104,on_lease_use_result:104,onboard:[6,19,38,228],onc:[2,6,13,15,16,18,19,21,22,24,28,29,30,31,32,33,37,39,40,45,46,48,49,60,61,62,63,66,67,69,113,135,173,195,213,214,224,228,234,239],one:[2,4,6,13,16,17,19,20,22,24,25,27,29,31,32,33,35,37,39,40,41,42,44,45,48,58,60,63,64,65,66,67,69,74,78,90,92,93,96,102,107,113,117,118,119,162,166,173,187,192,205,208,210,213,216,220,221,223,228,231,236,240],one_line_str:169,one_shot:230,one_shot_full_scan:230,oneof:[31,69,119,169],ones:[19,38,67,69,170],onli:[2,6,7,10,17,20,22,24,25,27,28,29,31,32,35,36,37,40,41,42,44,45,46,48,55,56,58,60,61,62,63,65,66,67,69,74,78,82,86,89,94,96,101,109,110,113,114,119,122,123,139,169,171,190,192,205,208,213,216,225,227,228,236,242,245],onlin:[29,43,60,61,62,227],onset:69,onset_timestamp:69,onto:[32,45,49,50,62,65,67,69,113,119,164,195,228,231],opaqu:37,open:[2,5,6,7,8,11,14,20,27,28,29,30,33,37,53,60,61,62,64,65,66,69,95,174,190,196,215,216,223,224,225,227,228,235,242],open_door:95,open_door_async:95,open_door_feedback:95,open_door_feedback_async:95,open_fract:119,open_port:225,opencv:[46,60,61,63,67,242],opendoor:69,opendoorcommandrequest:95,opendoorcommandrespons:95,opendoorfeedback:69,opendoorfeedbackrequest:95,opendoorfeedbackrespons:95,opendoorrespons:69,opengl:[67,235],oper:[4,5,6,8,9,10,12,13,17,18,19,21,22,23,24,40,42,44,45,48,50,53,58,60,65,66,67,69,83,88,90,91,96,100,107,118,192,204,205,218,220,224,227,229,231,233,235,241,242],operator_com:[69,118],operator_messag:69,operatorcom:[25,187],operatorcommentcommand:83,opportun:17,oppos:64,opposit:32,ops:104,opt:[40,190,227],opt_info:213,optic:69,optim:[2,9,67,69,109,197,215],optimizationfailureerror:109,optimize_anchor:213,optimize_existing_anchor:[69,213],optimized_anchor:213,optimized_anchoring_view:213,optimizer_param:69,optimzi:69,option:[2,6,17,18,22,24,26,28,29,30,31,32,35,36,43,44,45,46,55,58,60,61,62,63,64,65,66,73,74,82,83,86,88,93,94,100,104,118,119,122,123,125,126,129,148,149,154,192,213,214,220,221,224,225,227,231,233,236,238,240,242],orang:221,orb:69,order:[6,14,17,19,22,27,29,31,33,37,38,44,48,50,55,56,67,69,88,100,118,123,139,161,170,206,214,225,238,239],ordinarili:31,org:[55,56,65,69],organ:[36,61,69,231,233],orient:[2,6,7,21,22,27,32,41,45,53,66,67,69,117,119,161,208,213,240],origin:[2,6,17,27,29,32,37,41,43,48,66,67,69,86,213,233],original_error:97,original_valu:169,orthogon:64,osc:227,oscil:32,other:[0,2,6,7,9,10,14,17,18,19,20,22,23,24,25,27,29,30,31,32,33,36,37,40,41,42,44,45,47,48,49,52,58,61,62,65,66,69,74,76,84,85,87,89,90,91,92,93,94,95,99,100,101,102,104,107,108,110,114,115,118,119,123,139,140,148,164,190,192,199,201,208,216,221,225,227,232,236,240,242],other_data:69,other_leas:104,other_own:104,other_quat:110,otherwis:[2,13,17,21,22,27,32,40,62,63,69,84,85,96,97,99,104,115,118,121,122,136,166,190,214,222,225,227,242],our:[0,3,6,22,29,47,55,57,58,59,60,61,62,63,64,65,66,67,69,213],ourselv:62,out:[6,12,17,22,23,24,29,32,36,39,40,41,44,46,58,60,61,62,63,64,65,66,67,69,74,78,83,93,96,100,101,109,110,114,115,119,121,127,129,135,136,140,162,170,208,213,215,216,220,224,225,228,236,240],out_obj:62,out_proto:62,outag:67,outbound:55,outcom:36,outdoor:50,outer:[50,58],outermost:69,outfil:[144,148,160],outlier:67,outlin:[38,40,56,69],output:[2,27,28,34,43,44,46,53,61,62,63,64,65,66,67,69,81,83,88,101,102,107,110,177,189,215,221,225,231,233,242],output_directori:62,output_filenam:81,output_path:61,outsid:[10,21,29,31,38,40,44,45,58,69,109,221],outstand:69,outstretch:6,outward:32,oval:69,over:[2,3,6,7,16,17,20,24,29,31,35,39,42,44,45,51,52,55,57,58,60,61,62,63,66,69,94,115,117,119,126,157,172,173,175,177,195,213,223,225,231,233],overal:[19,31,32,37,52,67,69,88],overcom:69,overcurr:53,overh:[40,45],overhang:52,overhead:67,overlai:[67,69,226],overlap:[29,61],overmold:51,overrid:[2,46,67,102,108,119,130,143,164,166],overridden:[16,69,107,115,135],overriddenerror:115,override_external_force_vec:119,override_hold:69,override_mobility_param:69,override_not_hold:69,override_request:69,override_unknown:69,overridegrasp:69,overriding_messag:166,overview:[3,26,28,34,46,55,60,62,67,197,199,202,206,242],overwhelm:[69,227],overwrit:[31,60,69,100,117],overwritten:[6,69,115,228],own:[6,17,19,24,29,35,37,40,41,42,46,48,59,60,62,67,69,77,84,104,149,190,227],owner:[24,42,69],ownership:[2,24,42,67,69,94,100,115,117],p1_x:69,p1_y:69,p1_z:69,p_gnd:53,pace:[2,67],pace_2step_param:2,pack:[6,22,41,43,53,62,63,69,88],packag:[27,28,46,58,61,62,66,67,69,122,205,239,245],packet:[44,48,69],pad:[2,27,33,69,245],page:[24,30,37,41,48,49,55,60,61,62,63,64,67,69,113,118,191,192,213,224,228,242],page_id:[69,91],pai:50,paid:44,painstakingli:61,pair:[2,4,25,32,39,53,69,84,88,123,148,149,153,214],palm:[6,7,63,69],pan:[22,29,69,132,216,240],pan_limit:69,pane:29,panel:[27,171,172,174,175,176,177,178,179,180,181,182,183,184,185,217],pano:[35,67,69,230],panoram:[35,67],panorama:69,paradigm:44,parallel:[205,231,232],param:[1,2,27,31,63,64,67,73,79,84,85,86,87,99,109,119,123,164,213],paramet:[2,7,10,17,19,20,22,26,28,30,31,32,35,37,45,46,60,61,63,64,66,67,68,73,74,76,77,78,79,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,98,99,100,101,102,103,104,106,107,108,109,110,111,112,113,114,115,117,118,119,121,122,123,125,126,129,135,136,139,140,147,148,149,151,154,156,157,158,162,164,166,167,169,177,213,233],parameter:[45,69],parameter_nam:27,parameter_valu:69,parent:[6,41,42,46,66,67,69,83,99,143,169],parent_frame_nam:[69,99],parent_tform_child:[41,69,99],parentedg:41,park:21,parlanc:39,pars:[2,11,22,25,27,28,31,37,58,67,69,73,74,83,101,146,160,162,171,215,216],parse_arg:[60,62,63],parse_datetim:162,parse_timespan:162,parseerror:[146,147,151,154,155,158,160],parser:[27,31,60,62,63,83,139],part:[2,11,20,27,28,29,31,32,35,36,42,45,46,48,58,60,67,69,89,119,148,170,242],parti:[38,40,65,116,225],partial:[6,69,213],particualar:69,particular:[2,13,18,21,24,31,35,36,42,50,58,67,69,89,92,100,118,150,213],particularli:[29,43,67,223],partit:61,pass:[17,29,31,33,38,42,44,46,48,56,64,65,66,69,73,78,96,100,101,104,109,112,113,114,119,122,123,136,190,208,213,219,223,227,231,233,234,238,242,245],passphras:[69,129],password:[24,30,33,44,46,48,49,55,60,62,63,64,65,66,69,78,118,129,139,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,189,190,191,192,193,194,195,204,205,206,207,208,209,210,211,212,213,214,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,236,237,238,239,240,241,242,243,244,245],past:[17,63,67,69,88],past_tick:164,patch_level:69,path:[2,6,10,14,16,17,19,21,35,58,60,62,63,65,67,69,73,100,122,188,195,213,214,215,220,221,231,233,242],path_on_your_comput:65,path_to_downloaded_map:214,path_to_key_json:224,path_to_mission_fil:219,path_to_pb:[231,233],path_to_python3_execut:231,path_to_your_map_directori:[215,216],pathnam:65,pathwai:17,patrol:69,pattern:[32,44,58,69,225,231,233,245],pattern_alternate_color:2,pattern_fine_grained_alternate_color:2,pattern_flash:2,pattern_snak:2,pattern_unknown:2,paus:[18,22,45,67,69,164,177],pause_miss:164,pause_mission_async:164,pause_tim:69,pause_time_sec:164,pausemiss:[22,69],pay_reg_cli:113,payload:[0,3,6,20,21,22,24,31,34,36,40,44,45,51,54,55,57,59,68,75,83,93,98,102,115,118,139,170,190,224,227,229,242],payload_cli:48,payload_credenti:[69,190,227],payload_descript:228,payload_estimation_feedback:69,payload_estimation_request:69,payload_guid:[49,69,228,242],payload_guid_and_secret:[190,227],payload_ip:242,payload_nam:228,payload_ports_power_st:69,payload_ports_power_state_off:69,payload_ports_power_state_on:69,payload_ports_power_state_unknown:69,payload_proto:48,payload_registr:113,payload_registration_cli:[48,118],payload_registration_servic:75,payload_result:69,payload_secret:[49,69,223,228],payload_servic:75,payloadalreadyexistserror:113,payloadcli:[48,112],payloadcommand:83,payloadcredenti:67,payloaddoesnotexisterror:113,payloadestim:69,payloadestimationcommand:67,payloadlistcommand:83,payloadnotauthorizederror:113,payloadregistercommand:83,payloadregistr:[67,113],payloadregistrationcli:[48,113],payloadregistrationkeepal:113,payloadregistrationresponseerror:113,payloadregistrationservic:[67,113],payloadservic:[67,112],paylod:69,pazwierd:65,pb_enum_obj:169,pb_type:169,pbtxt:[61,62,63,64],pcba:50,pdb_root:[39,66,96],peak:8,peer:[44,66,69],penetr:69,peopl:[6,10,60,63,69,205,221,231,233],per:[2,4,17,22,27,28,29,31,32,38,40,41,61,67,69,213],perceiv:[67,69,205],percent:[69,101,102,208],percentag:[22,62,67,69,125,208],percept:[40,45,67,69,100,140,170,205,240],perfect:69,perform:[2,17,19,20,22,32,37,42,43,44,45,46,55,56,61,62,64,66,69,74,85,87,88,93,100,110,136,173,177,189,225,227,228,231,233,235,242],period:[16,21,24,32,42,44,45,46,66,67,69,73,77,93,94,96,113,115,129,135,219,224],period_sec:77,periodic_sec:77,perman:[20,188],permiss:[24,26,48,49,56,65,97,137,228],permission_requir:69,permissiondeniederror:[67,97],permit:[42,69],perpendicular:69,persist:[17,18,19,20,31,37,44,49,69,113,117,118,227],persistentrpcerror:[67,97],person:[8,60,63,64,202,227,231],person_model:64,perspect:[49,66,67],pertain:[32,40,67,69],pertin:40,pgm:[67,101],phase:[18,32,69],phone:227,php:[2,69],physic:[17,44,45,48,58,67,69,236],pick:[2,7,8,32,43,60,62,64,67,69,178,196,223],pick_auto_gaz:69,pick_auto_walk_and_gaz:69,pick_no_auto_walk_or_gaz:69,pick_object:69,pick_object_execute_plan:69,pick_object_in_imag:[63,69],pick_object_ray_in_world:69,pick_plan_onli:69,pick_vec:63,pick_walk_gaze_unknown:69,pickl:28,pickobjectinimag:63,pickup:[64,69],pictur:[15,41,53,55,60,63,65,67,69,178,184,227],pid:[56,62,69,96,228],pidfil:56,piec:[35,46,64,66,69,88,89,177,228],piecewis:69,pigz:222,piksi:[46,190],pil:[62,66,217],pil_imag:62,pillow:217,pilot:10,pin:54,pinch:50,ping:[36,55],pinhol:[63,69],pink:52,pinmax:53,pip3:231,pip:[60,61,171,172,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,204,205,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245],pipelin:[28,61,62,199,221,231],pipeline_config_path:[61,62],pitch:[2,27,41,48,66,67,69,110,161,245],pivot:32,pivot_cent:2,pivot_front:2,pivot_hind:2,pivot_unknown:2,pixel:[7,45,46,62,63,67,69,101,102,205,213,244],pixel_format:[62,69],pixel_format_depth_u16:69,pixel_format_greyscale_u16:[67,69],pixel_format_greyscale_u8:[62,69],pixel_format_rgb_u8:[62,69],pixel_format_rgba_u8:69,pixel_format_unknown:69,pixel_i:101,pixel_to_camera_spac:101,pixel_x:101,pixel_xi:[63,69],pixelformat:[46,67],pixels_per_met:213,pkla:56,pl_safety_in:53,pl_safety_o:53,place:[2,6,11,13,17,18,19,21,22,29,32,33,44,60,61,63,64,67,69,139,162,213,220],placehold:205,placement:[67,69],plai:[2,9,11,22,27,29,31,63,67,69,125,164,171,219,230],plain:[25,37,69],plan:[14,17,20,52,67,69],planar:[69,205],planar_ground:69,plane:[4,32,41,52,67,119],planned_point:69,planner:[67,69],planner_statu:69,planner_status_fail:69,planner_status_modifi:69,planner_status_success:69,planner_status_unknown:69,plastic:50,plate:[6,67,69],platform:[0,17,20,24,45,47,48,49,58,67,69,188,215],play_miss:164,play_mission_async:164,play_sound:125,play_sound_async:125,playabl:69,playback:[2,18,22,27,67,69,171,225],player:[22,29],playmiss:[22,69],playmissionrequest:14,playset:14,playsound:69,pleas:[0,17,21,29,36,46,55,56,65,69,78,107,109,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,190,194,204,205,211,217,224,227,231,232,233,242,245],plenti:[29,31],plot:233,plu:[22,36,67,69],plug:[55,69,227,242],pluge:190,plugin:[36,38,49,68,69,75,82,177,199,239],plugin_file_nam:190,plugin_test:[46,236],pluginerror:46,ply:[17,215],png:213,pod:[25,37,69,142,148],pod_series_read:155,pod_series_writ:156,pod_typ:[69,148,149,155,156],podseriesread:155,podserieswrit:156,podtypedescriptor:[148,149,155],podtypeenum:148,point1:62,point2:62,point3:62,point4:62,point:[2,6,7,10,17,21,22,24,25,32,41,44,45,47,50,55,56,59,60,61,62,63,64,65,68,69,75,100,101,110,117,118,119,122,162,188,197,205,206,213,216,220,227,231,233,244],point_cloud:114,point_cloud_request:[69,114],point_cloud_respons:69,point_cloud_servic:75,point_cloud_sourc:[69,114],point_cloud_source_nam:[69,114],pointcloud:[46,114,190],pointcloudcli:[114,239],pointclouddataerror:114,pointcloudrequest:114,pointcloudresponseerror:114,pointcloudservic:114,pointcloudsourc:114,pointer:13,polar:69,polici:[49,69,138,224,225],polkit:56,poll:[15,24,45,58,63,64,69,239],polycarbon:50,polyethylen:50,polygon:[62,63],polylin:[62,63],polynomi:69,pool:[44,88],poor:[10,44,62,69],poorli:[20,99],pop:[60,62,67],popul:[2,21,40,44,46,67,69,73,74,98,102,123,139,228],populate_response_head:123,popup:[67,208],port:[4,8,33,38,40,44,47,49,51,55,56,62,64,66,67,69,82,93,115,123,139,189,190,221,222,224,225,227,228,242],port_numb:[190,227,242],port_the_plugin_will_monitor:190,portain:[55,190,224,242],portbulk:53,portion:[10,32,61,62,69],pos:[17,27],pos_fl_rt_fram:119,pos_fr_rt_fram:119,pos_hl_rt_fram:119,pos_hr_rt_fram:119,pos_interp_cub:69,pos_interp_linear:69,pos_interp_unknown:69,pos_interpol:69,pose1:64,pose2:64,pose:[2,17,18,19,20,21,27,28,31,32,41,60,65,67,69,94,99,100,110,117,119,180,213,217,241,245],pose_3d:110,pose_dist:[63,64],pose_to_xyz_yaw:110,pose_trajectory_in_task:69,posit:[2,4,6,7,10,16,17,19,20,21,22,27,29,31,32,33,35,41,45,53,54,60,63,64,66,67,110,117,119,126,132,171,174,175,177,181,182,185,205,206,213,220,227,231,240],position_constraint:69,positions_carri:69,positions_readi:69,positions_stow:69,positions_unknown:69,possess:69,possibl:[2,6,8,10,15,17,18,20,24,27,29,31,38,40,44,45,50,55,58,64,66,67,69,85,87,97,100,166,177,190,192,195,213,221,225,227,242],post:[37,61,67,69,203,220,221],postiv:69,postprocess:[69,133],postur:32,potenti:[6,10,29,45,50,65,69,74,102],power:[8,10,15,22,24,25,28,29,31,33,39,47,48,49,52,55,58,62,65,68,75,83,96,104,113,118,119,121,124,135,171,173,195,204,205,214,220,225,227,228,232,234,236,241,242,245],power_cli:[115,118],power_command:115,power_command_async:115,power_command_feedback:115,power_command_feedback_async:115,power_command_id:[69,115],power_cycle_robot:115,power_off:[66,115,118],power_off_mission_servic:225,power_off_motor:115,power_off_payload_port:115,power_off_robot:115,power_off_wifi_radio:115,power_on:[22,66,115,118],power_on_miss:22,power_on_motor:115,power_on_payload_port:115,power_on_wifi_radio:115,power_pb2:22,power_servic:75,power_st:[22,66,69],power_statu:69,powercli:[115,131],powercommand:[58,69,83],powercommandfeedback:[58,69],powercommandrequest:[22,45],powererror:115,powerpayloadscommand:83,powerresponseerror:[115,118],powerrobotcommand:83,powerservic:[58,67,115,131],powerst:[45,66,67],powerwifiradiocommand:83,practic:[18,40,69],pre:[17,27,32,39,40,55,60,61,64,65,66,69,94,231,233],pre_move_cycl:[2,32],preced:[2,30,32,69],precis:[16,24,31,32,40,54,61,67,69,162],precise_step:[2,27],precise_steps_opt:72,precise_tim:[2,27],precise_timing_opt:72,precompil:61,precondit:58,preconfigur:56,predefin:[27,28,29,69,231,233],predetermin:[28,29],predict:[25,61,62],prefer:[38,46,58,66,69,192,242],preferred_joint_configur:69,prefix:[41,58,69,90,101,117,122,123,228],preinstal:[65,231],preload:49,premad:29,prep:[32,67,69,94],prep_pose_behavior:[15,69,94],prep_pose_only_pos:69,prep_pose_skip_pos:69,prep_pose_undock:[15,67,69],prep_pose_unknown:69,prep_pose_use_pos:69,prepar:[7,61,69,184],prepend:69,prerequisit:215,presenc:69,present:[27,28,35,44,53,66,67,69,92,178,184],preserv:[2,17,24,37,42,48,67,69,90],preset:[2,32,62,69,223,228],preset_configur:69,preset_custom:2,preset_exhaust:2,preset_fear:2,preset_interest:2,preset_nam:69,preset_nerv:2,preset_play:2,preset_unknown:2,press:[7,29,33,51,55,60,62,64,69,204,220,227,245],press_force_percentag:69,pretti:61,pretty_print:83,prevent:[6,15,17,20,24,27,31,40,42,44,45,46,50,66,67,69,96,99,100,104,123,139,205,213,227,232],preview:[26,33,208,227],previou:[2,10,17,22,27,31,32,39,40,60,61,62,63,64,66,67,69,90,93,94,136],previous:[56,67,69,78,82,90,94,96,104,115,119,148],previous_estim:69,previous_leas:69,previous_round_trip:[69,136],primari:[10,28,56,58,66,69,187],primarili:[32,37],primit:[20,44,69],princip:69,principal_point:69,principl:[40,66],print:[17,20,46,48,60,61,62,63,64,67,83,84,104,135,187,208,220,225,236,245],print_anchor:17,print_respons:84,prior:[16,19,21,31,32,45,67,69,225,231],prioriti:[44,65],prism:240,privat:[48,69,96,190,227,228,242],privileg:55,probabl:[60,61,69],problem:[16,24,40,44,55,58,61,62,67,69,74,79,84,85,87,88,90,91,92,93,94,95,96,98,100,101,106,107,109,111,112,113,114,115,118,119,121,136,140,147,151,154,155,158,160,164,230,235],problemat:44,proce:[13,37,55,88,104],procedur:[29,32,44,55,66,69,97,242],proces:69,process:[9,13,18,19,22,23,25,29,36,37,38,40,45,46,48,55,58,62,66,67,68,69,74,75,84,85,87,88,93,111,117,133,136,213,221,227,228,231,233],process_anchor:[109,213],process_img_req:63,process_kwarg:84,process_thread:62,process_topolog:109,processanchor:69,processanchoringrequest:[17,109,213],processanchoringrespons:[109,213],processed_images_queu:233,processing_delai:233,processing_skip:233,processor:[74,75,84,85,87,89,90,91,94,100,104,107,115,118,140,164,227,233],processtopolog:69,processtopologyrequest:[17,109],processtopologyrespons:109,produc:[7,19,20,28,31,50,61,69,109,177,190],product:[37,64,69,227],profil:32,program:[0,11,20,29,33,40,43,44,46,58,59,60,65,67,69,74,93,118,170,190,192,199,205,208,209,210,211,214,215,216,218,219,223,227,229,231,233,235,238,240,242,245],programm:[65,67],programmat:[36,40,67,69,223,228],progress:[45,58,67,69,88,170,171],project:[4,25,41,45,54,55,69,199],promis:63,prompt:[21,22,24,33,61,65,67,223],pronounc:[2,32],propag:67,proper:[29,38,42,53,65],properli:[6,37,39,40,48,55,65,67,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,188,194,195,213,217,227,228,233,234,245],properti:[2,22,45,64,67,69,74,77,84,96,104,110,118,119,136,140,143,144,148,149,150,151,152,154,155,156,157,159,160,164,244],proprietari:20,protect:[4,6,8,51,69,88],proto3:58,proto:[6,13,14,20,27,30,31,32,38,41,42,43,44,45,46,48,57,58,61,62,63,64,65,66,67,71,74,75,77,85,88,90,92,93,96,99,101,102,104,107,110,112,116,118,119,120,123,134,136,142,143,148,149,162,163,166,213,228,236],proto_enum_to_result_const:169,proto_from_result:169,proto_from_tupl:169,proto_messag:123,proto_msg:169,proto_nam:151,proto_object:73,proto_typ:[150,152],protobuf:[2,4,25,27,28,29,31,38,39,44,46,61,62,63,65,69,72,73,74,75,84,85,88,90,100,101,102,104,107,110,113,114,117,119,123,129,136,142,145,151,152,153,162,163,167,169,187,214,218,228,238,242],protobuf_channel_read:157,protobuf_class:151,protobuf_read:[157,158],protobuf_series_writ:159,protobuf_typ:[157,158,159],protobufchannelread:157,protobufread:158,protobufserieswrit:159,protoc:[61,228],protocol:[0,22,26,48,58,62,65,66,67,130,189,231],prototyp:69,provid:[0,2,3,4,7,9,11,13,15,16,17,18,19,20,21,22,24,25,26,28,29,30,31,32,33,36,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,61,64,66,67,69,73,74,78,82,84,85,87,88,94,96,97,100,101,102,104,110,113,114,118,119,122,123,166,167,169,190,204,205,208,213,214,221,222,225,227,228,230,231,236,238,242],provis:44,proxi:[67,97],proxim:[6,44],proxyconnectionerror:97,pseudo:[6,46,69],ptz:[22,35,67,68,124,131,230],ptz_desc:132,ptz_posit:69,ptzclient:[131,132],ptzservic:[67,132],publish:[49,67,69,107,224],pull:[6,53,64,69,233],pure:[24,25,48,69],purpl:226,purpos:[42,45,49,60,69,82,213,215,242],push:[7,65,67,69,231,233],push_point_in_fram:69,pushbar:[7,69],put:[2,6,10,27,46,50,58,60,61,62,64,67,69,118,119,214],puttext:[62,63],pwd:[189,227,242],pwr:53,pycocotool:61,pyjwt:70,pyopengl:239,pypi:65,pyplot:239,pyqt5:[60,187,213,239],pyqt:239,python3:[46,60,65,66,67,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245],python:[0,2,11,12,22,24,26,27,28,30,38,39,40,44,46,48,49,58,60,61,62,63,64,67,69,84,107,162,171,172,174,175,176,177,178,179,180,181,182,183,184,185,188,190,194,204,205,209,214,215,216,217,218,219,221,225,227,228,230,231,232,233,235,239,240,245],python_out:[61,228],python_type_to_pb_typ:169,python_var_to_valu:169,pythonpath:231,qt5:[213,239],qt5agg:213,qtopengl:239,quadrant:69,qualiti:[3,21,45,66,67,69,100,101,102,124,133,208,213,227,231],quality_perc:[46,69,101,102],quantiti:[58,69],quarter:29,quasi:[37,69],quat:[27,64,67,110,213],quat_to_eulerzyx:110,quaternion:[2,6,41,64,66,110],queri:[16,22,25,35,37,43,45,48,67,69,77,83,86,88,89,90,91,101,102,114,118,121,135,190,192,206,208,210,221,227,239],query_nam:77,query_param:86,queryabl:[118,129,242],question:[22,29,65,67,164,197],question_id:[69,164],questionalreadyansw:164,queu:[67,69],queue:[62,67,69,231,233],queue_statu:[67,69],queued_disk:69,queued_rend:69,queued_unknown:69,quic:44,quick:[24,55,63,65,67,69],quicker:227,quickli:[16,19,32,45,46,55,58,60,67,69,227,233],quickstart:[0,59,60,61,66,67,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,194,217,233,245],quiet:62,quit:[66,214],race:69,rad:[2,32,48,69],radial:[10,69],radian:[2,6,27,32,58,63,69,100,110,119,161],radio:[44,55,67,69,83,115],radiometr:[67,69],radiu:[2,32,51,69],rail:47,rais:[2,32,40,44,46,48,66,67,69,78,79,84,85,87,88,90,91,92,93,94,95,96,98,99,100,101,104,106,107,109,111,112,113,114,115,118,119,121,122,135,136,140,143,147,148,149,151,154,155,156,158,159,160,162,164],ram:[69,100],ramp:[69,175],ran:[64,69,189],random:[2,32,37,49,66,69,190,224,225,227,242],random_rotate_param:2,randomli:[2,61,69],randomrot:2,rang:[2,4,10,28,29,31,37,44,45,47,52,53,62,67,68,69,86,132,136,173,205],rapid:58,rapidli:[102,233,236],rare:93,rate:[2,24,28,44,67,69,97,227,231],rather:[2,6,17,27,35,44,45,55,58,63,67,69,119],ratio:[32,61],rational:58,raw:[25,41,45,46,62,66,67,69,101,102,216,231,233],raw_images_queu:233,ray_end_rt_fram:69,ray_properti:69,ray_start_rt_fram:69,raycast:69,rcnn:231,reach:[2,6,8,14,16,22,32,42,44,48,55,58,63,67,69,74,97,100,107,109,119,160,198,206,225,228],reachabl:69,react:[40,42,67],reaction:69,read:[0,3,6,22,25,29,31,37,42,44,48,57,58,59,65,67,69,72,73,74,83,96,104,112,114,122,137,143,147,149,150,151,154,155,157,158,160,166,227,231,233],read_and_find_animation_param:73,read_animation_param:73,read_anywai:69,read_checksum:[143,160],read_data_block:160,read_next_block:160,read_sampl:155,readabl:[27,28,31,36,45,46,69,90,117,118,228],reader:142,readi:[6,29,60,61,62,65,69,119,181,245],readili:[33,55],readm:[170,205],real:[13,17,18,29,38,44,60,62,67,69,109,231,233],realist:[31,67],realli:[64,66,69],realsens:69,rear:[15,44,48,49,52,69,119,171,172,174,175,176,177,178,179,180,181,182,183,184,185,217],rearrang:60,rearward:69,reason:[2,15,20,24,25,44,60,62,66,69,213,227],reboot:[17,19,28,49,67,69,224,227,228],recal:64,recalcul:69,recalibr:69,receiev:69,receiv:[2,6,7,10,24,28,31,38,39,42,43,44,46,48,65,66,67,69,78,79,82,90,93,96,100,107,118,119,122,123,139,174,205,225,231,233],recenc:104,recent:[2,10,28,32,42,45,46,65,66,67,69,74,104,114,214],recenter_angl:110,recharg:15,reciev:[69,74],reckon:69,reclaim:24,recogn:[20,46,60,67,69,97,162],recombin:31,recommend:[8,15,17,29,31,46,49,50,52,55,59,65,66,67,69,88,177,214,227,228,239],reconfigur:[113,221],record:[2,9,11,13,14,18,19,20,21,22,23,24,28,31,35,36,61,68,74,75,88,100,107,129,139,170,197,203,215,216,228,230],record_level:107,record_level_to_proto_level:107,record_to_msg:107,record_typ:129,recorddatablob:[37,69],recorddatablobsrequest:[67,123],recordev:[37,69],recording_command_lin:[11,17,18,67,214],recording_environ:[69,117],recording_servic:75,recording_session_id:2,recordingbufferful:74,recordingenviron:117,recordingserviceresponseerror:117,recordoperatorcom:[37,69],recordsignaltick:[37,69],recordsignalticksrequest:123,recordtextmessag:[37,69],recordtyp:129,recov:[19,67,69,131,195],recover:[25,69],recoveri:[19,69],recruit:6,rectangl:[32,60],rectangular:[69,240],rectilinear:69,red:[2,10,17,27,31,36,65,69,96,204,213,216,240],reduc:[6,20,21,29,45,46,49,52,67,69,74,224,231,233,242],reenter:62,refer:[0,2,4,9,11,12,13,17,20,21,26,27,29,35,36,41,44,45,48,49,53,57,59,65,67,69,100,110,171,190,204,213,216,224,227,231,242],referenc:[2,27,28,35,46,53,66,67,69,100,109,166,205],reference_id:[46,69],reference_index:69,reference_keypoint:69,reference_tim:69,refin:69,refine_fiducial_result_with_icp:[69,100],reflect:[42,67,69],refresh:[24,44,69,82,122,138],refresh_interv:133,refreshingaccesstokenauthmetadataplugin:82,refreshinterv:69,refus:[6,21,60,67,69],regain:67,regard:[22,29,67,69],regardless:[27,32,41,69,172],regen:53,region:[16,21,45,51,67,69,117],regist:[24,36,37,39,40,43,45,46,47,49,55,59,62,67,69,83,90,92,93,96,102,112,113,117,118,122,146,148,149,153,156,159,190,193,201,204,208,213,221,223,224,225,227,229,236,242],register_async:96,register_payload:[48,113],register_payload_and_authent:118,register_payload_async:113,register_service_cli:[63,122],register_signal_schema:90,register_signal_schema_async:90,register_with_robot:62,registerestopendpoint:69,registerestopendpointrequest:39,registerpayload:[48,69],registerservic:[48,69],registersignalschema:[37,69],registr:[24,38,40,45,49,55,62,66,67,68,69,75,83,90,98,170,190,201,224,225,227,236,242],registration_interval_sec:113,registri:[112,113],regrasp:69,regular:[2,29,40,45,66,69,77,104],regularli:[19,66,69,78,104,113],reiniti:69,reinstal:65,reject:[2,24,31,42,67,69,74,97,104,118,119],rel:[2,6,13,17,19,20,21,28,31,32,35,40,41,45,48,58,60,64,67,69,90,100,118,119,136,177,206,216,238],relat:[12,21,31,40,41,45,66,67,69,83,99,100,104,139,146,147,190,201,235],relationship:[17,41,66,69,213,216],releas:[0,2,5,17,21,25,28,29,30,33,36,37,44,46,49,55,69,107,204,205,220,227],relev:[21,39,40,41,69,165],reli:[16,17,18,19,21,45,88],reliabl:[24,29,32,40,44,60,67],reload:56,reloc:67,remain:[2,15,16,31,32,45,66,67,69,122,205],remainafterexit:56,remaining_rout:69,remap:69,remapping_const:69,remedi:69,rememb:[21,29,40,65,69,171,172,174,175,176,177,178,179,180,181,182,183,184,185,217],remind:[29,33,227],remot:[22,38,42,44,55,56,66,67,68,97,107,117,163,188,197],remote_cli:167,remote_cloud_statu:69,remote_mission_cli:225,remote_mission_servic:[11,67],remote_servic:163,remotecli:[67,163,167,225],remotecloudfailurenodataerror:117,remotecloudfailurenotindirectoryerror:117,remotegrpc:22,remotemiss:[69,167],remotemissionservic:[11,12,67,167],remoteservic:48,remount:69,remov:[19,20,29,33,45,53,55,62,69,86,88,93,104,107,122,123,129,193,205,223,227,232],renam:[63,65],rench:69,render:[69,240],rendit:41,renew:67,repeat:[20,22,24,31,32,38,46,54,67,99,139,205,208,240],repeatedli:40,repetit:69,replac:[17,55,60,61,64,67,69,96,115,227,231],replai:[9,10,14,16,18,19,20,36,67,197],replan:14,replay_miss:[67,219,226],repli:[62,69],repo:[0,205],report:[15,19,21,36,40,41,45,58,62,63,64,67,69,88,135,233],reposit:[67,69,119,234],repositori:[0,61,122,205],repres:[2,17,19,20,25,27,31,40,41,42,44,45,46,49,58,66,67,69,73,74,77,81,90,96,99,100,101,102,104,110,117,119,122,136,161,162,214,216,224,228,242,244],represent:[22,28,29,35,44,45,66,67,69,99,169],reproject:[45,69],req:[44,84,123],reqeuest:69,request:[2,5,7,11,14,17,22,24,25,31,37,38,39,41,42,44,45,46,48,51,52,55,58,62,63,64,66,67,74,76,79,83,84,85,86,87,88,92,93,95,96,97,100,101,102,104,106,108,111,113,114,115,116,117,118,119,121,123,135,136,140,145,150,153,174,176,178,179,181,182,183,185,190,208,210,214,221,227,228,236,242,244],request_attach:69,request_cycle_robot:69,request_detach:69,request_head:[44,66,69],request_id:[69,85,86,87,88],request_iter:84,request_live_imag:[69,100],request_live_point_cloud:[69,100],request_live_robot_st:[69,100],request_live_terrain_map:[69,100],request_live_world_object:[69,100],request_manag:88,request_name_in_blackboard:69,request_off:[45,69],request_off_motor:69,request_off_payload_port:69,request_off_robot:69,request_off_wifi_radio:69,request_on:[22,45,69],request_on_motor:69,request_on_payload_port:69,request_on_wifi_radio:69,request_preserv:69,request_queu:62,request_received_timestamp:[44,66,69],request_timestamp:[66,69],request_trim_for_log:84,request_unknown:69,requestabortederror:100,requestcancellederror:88,requested_slic:2,requestfailederror:100,requesthead:[2,44,58,123],requestiddoesnotexisterror:[85,87],requestmanag:88,requestor:69,requestst:88,requir:[2,7,13,15,16,17,18,20,22,24,25,26,27,29,31,32,37,38,39,40,42,44,45,46,47,48,49,50,54,55,56,60,61,62,63,64,66,67,69,74,85,94,97,113,115,116,117,119,139,167,171,172,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,204,205,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,223,225,226,228,229,230,231,232,233,234,236,237,238,239,240,241,242,243,244,245],require_align:[69,117],require_fiduci:[69,117],require_protobuf:154,required_rol:96,requirements_cli:221,requirements_server_cpu:221,requirements_server_gpu:221,requires_arm:27,rerout:38,rerun:227,res:242,rescal:[2,27],research:[61,62,64,221],resend:[16,45,100],reserv:[2,25,37,58,69],reset:[39,53,67,69,93,97,131,132,216,224,228,240],reset_servic:93,reset_service_registr:93,resett:53,reshap:[62,63],resili:67,resist:[8,53,69],resistor:53,resiz:29,resnet50:61,resolut:[48,67,69,215,227,231,242],resolv:[40,65,67,69,97,122],resons:69,resort:14,resourc:[24,62,66,69,100,104,107,117,122,167,242],resource_already_claim:69,resource_list:104,resource_path_glob:122,resource_tre:69,resourcealreadyclaimederror:104,resp:[63,84,123],respect:[2,6,13,21,27,32,37,41,45,66,67,69,110,119,196,203,213,221],respond:[22,31,38,39,40,42,45,46,48,58,66,67,69,88,102,111,236],respons:[2,6,15,21,31,37,39,40,44,46,58,62,63,65,66,67,69,74,76,77,78,79,84,85,87,88,89,92,93,96,97,98,100,101,104,109,111,113,114,115,116,117,118,119,121,122,123,135,136,137,140,145,150,153,164,167,188,190,208,213,227,228,236],response_from_challeng:96,response_iter:84,response_queu:62,response_timestamp:[44,66,69],response_trim_for_log:84,responsecontext:[67,123,168],responseerror:[67,74,78,79,84,85,92,93,94,96,97,98,100,101,104,109,111,113,114,115,117,118,119,135,164,167],responsehead:[2,44,58,123],responsetoolargeerror:[67,97],rest:[24,25,35,36,37,44,58,60,67,69,86,88,190,236],restart:[2,22,49,55,64,67,69,74,93,107,113,164,169,190,222,224,225,227,242],restart_after_stop:69,restart_miss:164,restart_mission_async:164,restartmiss:[22,69,164],restartwhenpaus:67,restor:[10,61],restrict:[13,29,69],restrict_fiducial_detections_to_target_waypoint:[67,69],result:[2,13,16,17,19,21,24,27,29,31,35,40,42,43,52,60,61,62,63,66,67,74,77,84,88,100,104,120,135,165,169,189,190,208,221,225,227,242],result_constant_to_proto_enum:169,result_error:69,result_failur:69,result_run:69,result_success:69,result_unknown:[69,169],resultact:56,resultani:56,resultfromproto:169,resultinact:56,results_from_proto:169,resum:[44,67,69],resume_fail_when_not_on_rout:69,resume_return_to_unfinished_rout:69,resume_unknown:69,retain:[24,42,66,69,104],retain_leas:104,retain_lease_async:104,retainleas:[42,67,104],retarget:69,reticl:126,retime_to_integer_slic:[2,27],retime_to_integer_slices_opt:72,retinanet:[221,222],retinanet_serv:222,retri:[22,65,67,94,97,195],retriev:[34,36,37,40,46,67,69,86,92,102,104,122,125,127,128,129,131,134,147,190,191,208,230],retrieve_raw_data:129,retrieverawdata:69,retryablerpcerror:[67,97],retryableunavailableerror:[65,67,97],return_at_exit:104,return_leas:[63,64,104],return_lease_async:104,return_to_start_pos:[2,32],returned_leas:104,returnleas:[42,69],reupload:[2,74],reus:[17,55,109],revamp:67,reveal:15,revers:[2,17,32],revert:69,review:[0,12,31,37,55,69,227,235],revis:[61,65,66],revoc:42,revok:[24,42,69,104,135],revokedleaseerror:104,revolut:69,rfc:190,rgb24:69,rgb:[2,8,32,62],rgba:69,rhs:[22,69],rich:20,ricoh:[38,46,49,67,199,202,236],ricoh_client_mod:[67,227],ricoh_theta:[188,227],ricoh_theta_image_servic:227,ridg:51,riff:69,right:[2,4,22,29,32,33,36,45,52,53,54,55,60,61,63,64,66,67,69,84,85,87,102,118,119,171,205,208,213,216,220,240,241,245],right_color:[2,32],right_depth:66,right_depth_in_visual_fram:66,right_fisheye_imag:[60,63,66],right_fisheye_image_0000:60,right_fisheye_image_0001:60,right_fisheye_image_0004:60,right_fisheye_image_0009:61,right_fisheye_image_0027:61,right_fisheye_image_0076:60,right_fisheye_image_0077:60,right_hip_i:[2,32],right_hip_x:[2,32],right_kne:[2,32],right_same_as_left:[2,32],rightsid:67,rigid:[50,69],ring:69,rippl:32,ripple_color_param:2,rise:[4,52,53,69],risk:[6,66,69],rj45:44,rle:[45,69,101],rle_count:69,robin:44,robot:[2,3,6,7,8,9,10,11,12,13,17,18,20,22,23,25,26,27,28,30,31,32,35,36,37,39,40,42,43,46,47,48,49,50,54,55,58,60,62,63,64,68,69,71,73,74,75,76,77,78,81,82,83,84,85,86,87,88,90,91,92,93,94,95,96,97,98,100,101,102,103,104,105,106,107,108,109,111,112,113,114,115,117,122,124,131,135,136,138,139,140,141,142,162,164,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,187,189,190,191,194,196,198,202,204,206,208,211,214,215,216,217,219,220,222,223,224,227,228,229,231,232,233,235,236,237,238,239,240,242],robot_clock:[81,162],robot_clock_skew_nsec:162,robot_command:[22,63,64,66,119,140],robot_command_async:119,robot_command_feedback:[63,119],robot_command_feedback_async:119,robot_command_id:[69,119],robot_command_pb2:[22,119],robot_command_servic:75,robot_hostnam:[84,231,239],robot_id:[25,66,120],robot_id_servic:75,robot_impair:69,robot_ip:[30,46,49,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,189,190,192,193,194,195,204,205,206,207,208,209,210,211,212,213,214,217,218,219,220,221,222,223,225,226,227,228,229,230,231,232,233,234,236,237,238,240,241,242,243,244,245],robot_kinemat:[35,69],robot_local_grid:69,robot_metr:69,robot_password:189,robot_power_st:69,robot_power_state_off:69,robot_power_state_on:69,robot_power_state_unknown:69,robot_rt_person_ewrt_vis:64,robot_rt_person_ewrt_vision_hat:64,robot_seconds_from_local_second:162,robot_seri:69,robot_st:[22,63,64,121],robot_state_cli:[63,64],robot_state_miss:22,robot_state_pb2:121,robot_state_servic:75,robot_tim:[81,136],robot_time_range_from_datetim:136,robot_time_range_from_nanosecond:136,robot_timestamp:[90,107],robot_timestamp_from_loc:162,robot_timestamp_from_local_nsec:162,robot_timestamp_from_local_sec:[136,162],robot_us:189,robotcommand:[7,22,64,66,67,119],robotcommandbuild:[63,64,66,67,119],robotcommandcli:[63,66,115,119,182],robotcommandissueserror:74,robotcommandrespons:67,robotcommandresponseerror:[115,118,119],robotcommandservic:[66,115,119],roboterror:[67,118],robotfaultederror:100,robotget:11,robothardwareconfigur:69,robotid:[24,67,118,187],robotidcli:[66,120],robotidcommand:83,robotidrequest:[25,66,187],robotidrespons:187,robotidservic:[66,120],robotimpairederror:100,robotimpairedst:67,robotiniti:11,robotlinkmodel:69,robotlosterror:100,robotmodel:83,robotnotlocalizedtorouteerror:100,robotsoftwarereleas:24,robotst:[44,67,121],robotstatecli:[63,66,115,121],robotstatecommand:83,robotstateerror:100,robotstateservic:[66,121],robotstuckerror:100,robottimeconvert:[136,162],robottimeconvertor:136,robust:[19,31,44,47,51,63,67,69],robustli:[14,52],robutt:[2,32],role:[39,66,69,96],roll:[2,27,32,41,48,66,67,69,110,119,161,195,245],room:[43,156,213],root:[6,13,22,24,40,41,42,44,66,67,69,99,139,164,166,169],root_frame_nam:[6,69],root_tform_task:[6,69],rope:63,rot:[64,110,213],rot_matrix:213,rotat:[2,4,6,29,31,32,41,43,60,63,64,66,67,69,110,111,171,208,213,216,221,240,245],rotate_180:60,rotate_90_clockwis:60,rotate_body_param:2,rotate_imag:[63,69],rotate_image_align_horizont:[63,69],rotate_image_align_with_bodi:69,rotate_image_no_rot:69,rotate_image_unknown:69,rotation_ewrt_fram:69,rotation_multipli:[2,27],rotation_set:69,rotation_setting_absolut:69,rotation_setting_offset:69,rotation_setting_unknown:69,rotation_with_toler:69,rotational_spe:[69,119],roughli:[21,69],round:[31,44,69,135,136,237],round_trip_tim:[69,136],rout:[9,11,13,14,16,18,21,23,38,48,55,67,93,100,117,214,228],route_blocked_behavior:69,route_blocked_fail:69,route_blocked_rerout:69,route_blocked_unknown:69,route_follow_param:[69,100],route_gen_param:69,route_param:[69,100],routeerror:100,routefollowingparam:67,routegenparam:100,routenavigationerror:100,routenotupdatingerror:100,router:69,routin:[26,29,30,31,33,67,69,135,238],row:[29,35,46,63,69,102],rpc:[2,13,15,16,17,20,21,24,27,28,31,36,37,38,42,44,45,46,58,66,67,69,74,78,79,82,84,85,86,87,88,89,93,96,97,100,104,107,113,118,121,122,123,126,136,164,190,198,213,227,228,236,242],rpc_error:82,rpc_interval_second:[93,96,104],rpc_logger:123,rpc_method:[66,84],rpc_timeout:107,rpc_timeout_sec:113,rpc_timeout_second:[93,96],rpcerror:[67,79,82,85,87,88,90,91,92,93,94,95,96,97,98,100,101,106,107,109,111,112,113,114,115,118,119,121,136,140,164],rssi:69,rubi:[2,69],rule:[17,60],ruler:213,run:[2,3,4,10,11,13,15,19,20,22,24,25,28,31,32,33,35,38,40,41,43,44,45,55,56,57,59,60,61,62,63,64,66,67,69,74,77,82,83,84,86,88,100,111,123,135,136,164,165,169,170,197,214,219,220,221,222,228,233,236,245],run_camera_calibr:135,run_constrained_manipul:173,run_on_clos:148,run_spot_check:135,run_until_interrupt:[38,123,139],runawai:69,runner:[123,139],running_man_param:2,runningman:2,runtim:[4,17,67,69,83,242],runtime_var:69,runtimeerror:113,rx_axi:69,rx_beacon_signal_avg_dbm:69,rx_bits_per_second:69,rx_byte:69,rx_packet:69,rx_signal_avg_dbm:69,rx_signal_dbm:69,rxrx:69,rxry:69,rxrz:69,rxx:69,rxy:69,rxz:69,ry_axi:69,ryi:69,ryri:69,ryrx:69,ryrz:69,ryx:69,ryz:69,rz_axi:69,rzrx:69,rzry:69,rzrz:69,rzx:69,rzy:69,rzz:69,safe:[6,21,22,25,39,40,61,62,64,66,67,69,96,104,115,118,119,136,225,242],safe_pb_enum_to_str:169,safe_pb_type_to_str:169,safe_power_cycle_robot:[67,115],safe_power_off:[66,67,115],safe_power_off_command:119,safe_power_off_feedback:69,safe_power_off_motor:[67,115],safe_power_off_request:69,safe_power_off_robot:[67,115],safe_stop:6,safepoweroff:69,safepoweroffcommand:45,safeti:[15,16,42,45,52,66,231],sagitt:32,sai:[31,39,41,66,67,69,164],sale:65,same:[6,17,20,21,22,24,27,28,29,30,31,32,33,35,36,38,39,40,41,42,43,44,45,46,48,50,53,60,61,63,64,65,66,67,69,74,85,87,90,97,100,102,104,119,172,188,190,208,216,217,221,225,227,228,242],sampl:[20,48,69,156,188],samplecommon:67,sand:69,sat:65,satisfi:[25,62,69,89],save:[2,9,17,19,22,23,25,28,31,35,36,37,46,49,60,61,62,67,69,73,74,83,85,87,88,101,118,137,171,187,189,190,208,213,215,219,220,222,223,224,230,236,238],save_choreography_sequence_to_fil:74,save_images_as_fil:101,saved_model:[62,63,64],saw:62,scalar:69,scale:[2,18,20,27,32,63,69,126,173,213],scale_factor:69,scallop:52,scan:[20,46,48,69,117],scan_match_region:69,scenario:[28,31,203],scene:[19,45,67,69,140,240],schema:[37,69,90,169],schema_id:[69,90],schema_nam:[37,69,90],schema_sourc:37,schemat:53,scheme:[18,24,45],scope:[22,42,58,66,69,221],score:[62,69],scp:49,scratch:61,screen:[14,36,62,65,67,69,126,208,227,233,240],screenshot:[36,49],screw:54,script:[2,27,28,29,31,43,46,48,60,61,62,63,64,65,67,123,139,173,174,177,187,188,190,195,220,221,223,224,226,227,234,236],scroll:29,sculpt:50,sdk:[3,4,5,9,11,12,14,17,19,20,24,30,36,38,40,42,44,45,46,47,48,49,57,59,60,62,63,67,74,75,77,82,118,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,189,190,194,195,202,204,205,212,213,217,226,227,230,231,232,233,234,236,237,238,239,243,244,245],sdkerror:122,se2:[6,69,99],se2_fame_nam:119,se2_frame_nam:[69,119],se2_trajectory_feedback:[63,69],se2_trajectory_request:[6,69],se2_vel_vector:110,se2_velocity_feedback:69,se2_velocity_in_b:110,se2_velocity_request:69,se2pos:[99,100,110,119],se2trajectorycommand:[63,119],se2veloc:[64,99,110],se2velocitycommand:67,se2velocitylimit:[64,67,100,117],se3:[2,69,110],se3_covari:69,se3_vel_vector:110,se3_velocity_in_b:110,se3covari:67,se3pos:[2,17,21,31,41,64,67,99,100,110,117,213],se3trajectori:[6,119],se3trajectorypoint:67,se3veloc:[99,110],seal:50,search:[7,13,55,63,64,69],search_ray_end_in_fram:69,search_ray_start_in_fram:69,seat:[32,66],sec:[63,67,69,162],sec_to_nsec:162,second:[2,6,15,21,22,24,25,27,28,29,31,32,33,37,39,42,44,45,46,52,58,63,64,65,66,67,69,74,77,88,93,96,100,102,104,107,113,117,118,119,121,123,136,139,162,175,187,190,192,213,214,225,230,231,233],second_foot:[2,32],secondari:[2,32,45,69],seconds_to_dur:162,seconds_to_timestamp:162,secret:[46,48,49,67,69,113,118,190,223,224,227,228,229,242],secs_to_hm:162,section:[5,10,20,22,26,29,30,31,33,34,36,45,47,48,49,52,56,60,61,63,64,65,66,67,69,73,190,208,213,227,228,231,233,242,245],secur:[15,20,25,37,44,54,56,65,66,82],see:[0,2,5,6,7,13,14,17,21,22,24,29,31,33,36,39,42,43,55,56,60,61,62,63,64,66,67,69,78,93,94,96,107,115,118,119,120,122,169,171,172,178,184,186,187,188,189,190,192,193,195,196,197,198,199,201,202,203,204,205,206,209,211,212,213,214,215,217,218,219,220,224,225,226,227,228,229,230,232,234,237,238,239,241,242,243,244],seed:[17,21,67,69,100,215],seed_tform_bodi:[17,21,69],seed_tform_go:[69,100],seed_tform_object:[69,213],seed_tform_object_constraint:69,seed_tform_object_uncertainti:69,seed_tform_waypoint:[17,69],seed_tform_waypoint_constraint:69,seed_tform_waypoint_uncertainti:69,seek:[25,50,67,160],seekabl:147,seem:97,seen:[31,32,44,49,61,63,66,67,69,104,208,213,220,226,228],segment:[17,60,69],segreg:69,select:[6,7,27,31,32,33,37,44,49,53,55,60,62,63,64,65,67,69,100,106,126,147,174,178,187,190,223,224,225,227,242],selector:[22,29,31],selector_miss:22,self:[2,19,21,22,32,33,44,45,46,52,62,67,69,82,83,104,110,161,170,171,190,201,213,220,225,227,228,241,242,245],self_own:104,self_register_payload:228,self_register_servic:228,self_registr:67,selfright:[66,69],selfright_command:119,selfright_feedback:69,selfright_request:69,semant:[24,42,45,66,221],semi:7,send:[6,17,24,29,31,33,36,37,39,42,43,44,45,48,52,62,63,64,67,69,74,82,83,86,88,93,100,107,113,118,122,136,172,213,221,225,228,242],sender:48,sens:[0,17,46,47,69,109],sensit:[6,50,66,220],sensor:[0,3,6,8,14,17,18,19,20,21,22,24,36,37,38,41,45,46,57,59,63,67,69,102,103,190,194,214,223],sensori:13,sent:[2,6,24,29,35,36,39,42,44,45,46,48,63,69,74,90,96,118,119,123,139,187,228],separ:[27,31,35,36,46,58,63,65,67,69,73,130,137,172,174,175,176,177,178,179,180,181,182,183,184,185,189,206,217,233,238,242],sepcif:69,seper:69,seq:230,sequenc:[1,2,6,15,18,24,25,27,28,31,32,33,37,42,66,67,73,74,90,104,107,115,118,169,171,203],sequence_id:[69,90],sequenti:69,seri:[17,27,31,37,53,67,69,91,142,143,145,146,147,148,149,150,151,152,153,154,157,158,160,187,190,213,236],serial:[2,24,25,28,44,65,67,69,74,153,159,191,214,218],serial_numb:[24,66,69],serializ:90,serializetostr:46,series_block_file_offset:149,series_block_index:[69,147,149,160],series_block_index_offset:69,series_descriptor:[69,147,149,150,152,155,157,160],series_identifer_hash:69,series_identifi:[69,149],series_identifier_hash:69,series_identifier_to_hash:149,series_index:[69,147,148,149,150,151,152,154,158,160],series_index_to_descriptor:154,series_spec:[143,146,148,149,155,156,159],series_spec_to_index:143,series_typ:[25,69,145,146,148,149,150,152,156,159],seriesblockindex:[147,149,160],seriesdescriptor:[147,149,154,155,157,160],seriesidentif:69,seriesidentifi:[25,145,146,149],seriesnotuniqueerror:[146,148,149],seriou:84,serv:[69,93,137,228],server:[3,18,37,39,40,44,55,57,62,63,64,67,69,75,78,82,96,97,100,104,109,111,130,136,139,163,167,189,190,213,224],server_config:[63,69],server_cpu:221,server_data:63,server_error:69,server_ip:[189,221],server_rx:69,server_tx:69,server_util:[67,123,168],servererror:97,servic:[1,2,3,5,9,11,13,16,17,19,20,21,25,26,28,30,34,35,41,43,44,47,49,56,57,58,59,62,63,64,65,68,74,75,76,78,79,81,82,83,84,85,86,87,89,90,92,93,94,95,96,97,98,99,100,101,103,104,106,107,108,109,111,112,113,114,115,117,118,119,120,121,122,123,125,126,127,128,129,130,131,132,133,134,135,136,139,140,142,145,163,164,165,167,170,171,187,188,196,197,199,200,202,203,205,211,213,218,221,224,225,238,243],service_author:38,service_cli:122,service_entri:69,service_error:69,service_fault:[98,229],service_fault_id:98,service_fault_pb2:102,service_fault_st:69,service_ip:38,service_nam:[22,63,69,92,102,118,122,145,152,153,221,236],service_port:38,service_read:150,service_runn:38,service_servic:[38,123,139],service_typ:[38,40,74,76,78,79,85,87,88,89,90,91,92,93,94,95,96,98,100,101,103,104,105,106,107,108,109,111,112,113,114,115,117,118,119,120,121,122,125,126,127,128,129,130,131,132,133,134,135,136,140,164,167],servicealreadyexistserror:93,servicedoesnotexisterror:93,serviceentri:[24,48],servicefailedduringexecutionerror:97,servicefault:[98,102],servicefaultalreadyexistserror:98,servicefaultdoesnotexisterror:98,servicefaultid:98,servicefaultst:67,serviceservic:[123,139],serviceunavailableerror:97,session:[2,22,35,36,44,56,69,74,167],session_id:[2,69,167],set:[2,6,10,14,15,16,19,20,22,24,25,27,28,29,31,32,33,35,36,37,39,40,42,43,44,45,46,48,49,56,58,60,61,62,63,64,65,66,67,69,72,73,74,77,79,81,82,86,88,90,96,101,102,104,107,110,111,113,116,117,118,119,122,123,125,126,128,129,130,132,133,137,139,148,149,162,164,169,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,188,191,194,205,214,217,223,224,225,227,230,231,233,239],set_audio_capture_channel:125,set_audio_capture_channel_async:125,set_audio_capture_gain:125,set_audio_capture_gain_async:125,set_challeng:96,set_client_nam:104,set_color_param:2,set_colormap:230,set_complete_if_no_error:88,set_config:96,set_config_async:96,set_focu:230,set_ic:230,set_ice_configur:130,set_ice_configuration_async:130,set_ir_colormap:126,set_ir_colormap_async:126,set_ir_en:103,set_ir_enabled_async:103,set_ir_meter_overlai:126,set_ir_meter_overlay_async:126,set_last_captured_imag:102,set_led_bright:128,set_led_brightness_async:128,set_loc:100,set_localization_async:100,set_localization_request:69,set_logg:102,set_max_message_length:122,set_passphras:129,set_passphrase_async:129,set_posit:230,set_power_statu:131,set_power_status_async:131,set_proto:73,set_ptz_posit:132,set_ptz_position_async:132,set_ptz_veloc:132,set_ptz_velocity_async:132,set_recording_environ:117,set_recording_environment_async:117,set_response_head:67,set_screen:126,set_screen_async:126,set_statu:[46,88],set_stream_param:133,set_stream_params_async:133,set_timestamp_from_datetim:162,set_timestamp_from_now:162,set_timestamp_from_nsec:162,set_volum:[125,230],set_volume_async:125,setaudiocapturechannel:69,setaudiocapturegain:69,setblackboard:22,setestopconfig:69,setestopconfigrequest:39,seticeconfigur:69,setircolormap:[67,69],setirmeteroverlai:[67,69],setledbright:69,setloc:[18,20,69],setlocalizationrequest:67,setlocalizationrespons:67,setpassphras:69,setposit:69,setpowerstatu:[67,69],setptzfocu:67,setptzposit:69,setptzveloc:69,setrecordingenviron:[18,69],setscreen:69,setstreamparam:69,settl:69,settle_then_cut:[39,45,96],settle_then_cut_async:96,setup:[26,33,56,60,61,63,139,188,197,227,233,242,245],setup_log:139,setup_token_cach:118,setvolum:69,sever:[24,36,39,45,66,67,119,227,235],severity_crit:69,severity_info:69,severity_scor:69,severity_unknown:69,severity_warn:69,sfixed32:[2,69],sfixed64:[2,69],sh0:[27,32,69,119],sh0_handler:72,sh1:[27,69,119],sh1_handler:72,sha1:[25,69],shade:69,shader:[67,235],shadow:[20,69],shall:58,shape:[32,50,62,69],share:[17,22,24,45,69,104,143,188,205,214,216],sharp:32,sheet:20,shelf:[60,64],shell:[53,187],shield:51,shift:[29,32,132],shift_max_transition_tim:[2,32],shift_mean_period:[2,32],shift_pan_angl:132,shine:232,ship:[24,38,55],shock:[69,129],shoe:63,shoould:69,shore:[66,69],shore_power_st:69,shore_power_state_off:69,shore_power_state_on:69,shore_power_state_unknown:69,shorepowerconnectederror:115,shortcut:69,shorten:[2,27],shorter:69,shortest:[67,69],shorthand:66,shot:[60,63,66,69],should:[2,6,13,15,16,17,19,20,21,24,25,28,29,31,32,33,37,38,39,40,41,42,43,44,45,46,48,49,50,51,52,53,54,55,56,58,60,61,62,63,64,65,66,67,69,73,74,84,87,88,90,93,96,100,102,104,107,109,110,111,113,115,118,119,122,123,131,135,136,164,172,178,184,188,189,192,208,213,215,216,217,220,223,224,225,227,228,230,232,233,236,242],should_exit:136,shoulder0:27,shoulder1:27,shoulder:[32,69],shoulder_0:2,shoulder_0_offset:[2,27],shoulder_1:2,shoulder_1_offset:[2,27],shouldn:[69,117,215],show:[2,4,7,17,22,24,25,26,27,28,29,31,32,38,42,44,45,46,48,49,61,63,65,66,67,69,83,100,117,139,172,178,182,184,187,188,190,191,196,198,199,200,201,202,203,206,208,213,216,225,226,227,229,233,235,238,240,242],show_progress:81,shown:[14,17,22,24,28,29,31,33,36,37,38,41,42,44,49,50,52,53,61,64,67,69,213,214,231,233,240],shrink:29,shrunk:29,shut:[104,123,136,139,225],shutdown:[69,93,96,104,113,123,139,225],shutsdown:123,shutter:69,side:[19,29,32,45,49,50,51,67,69,119,172,174,175,176,177,178,179,180,181,182,183,184,185,217],side_left:2,side_param:2,side_right:2,side_unknown:2,sidewalk:[7,67,177],sight:220,sigint:[38,123,139,242],sign:[2,24,32,45,65,69,225],signal:[24,42,53,69,90],signalschema:90,signaltick:90,signatur:[29,46,88],signfic:69,signific:[44,52,67,69,213],significantli:[19,21,24,45,69],silent:67,similar:[7,20,32,33,36,45,55,66,67,69,104,110,177,191,204,222,242],similarli:[6,41,67,69],simpl:[6,25,37,56,59,60,65,67,69,177,183,196,226,228,231,242],simpleparallel:22,simpler:[44,206],simplest:[16,39,66,119],simpli:[15,17,29,33,42,65,69,188],simplic:66,simplifi:[24,36,37,40,44,46,58,67,88,196,203,214],simul:177,simultan:[31,67,69,172],sin:213,sinc:[2,6,21,24,32,41,42,44,45,55,59,61,62,64,66,67,69,74,84,115,118,119,122,136,148,156,159,162,187,190,192,206,208,225,227,231,242],singl:[2,6,13,20,22,24,27,29,31,32,35,37,39,40,41,42,44,54,60,66,67,69,74,88,90,94,96,99,102,104,110,122,148,157,208,213,228,244],sint32:[2,69],sint64:[2,69],sip:60,sit:[2,4,10,15,22,29,31,33,39,45,52,53,66,67,69,96,107,115,119,135,171,172,174,175,176,177,178,179,180,181,182,183,184,185,195,214,217,220,232,234,241,245],sit_command:[67,119],sit_feedback:69,sit_miss:22,sit_request:[22,69],sitcommand:22,site:[0,3,17,18,21,47,57,59,66,67,205,239],situat:[6,14,21,29,66,67,69],six:[24,54],size:[2,16,20,25,32,45,60,67,69,84,97,110,117,148,164,205,208,214,231,233],size_ewrt_fram:69,skeleton:[45,121],skew:[66,69,110,136,162,237],skew_matrix_2d:110,skew_matrix_3d:110,skink:214,skip:[7,67,69,205,233],skip_app_token_check:118,sky:69,sleep:[22,60,63,64,67,227,231,233],slerp:110,slew:67,slew_rate_limit:69,slice:[2,27,29,32,74],slices_per_minut:2,slide:32,slider:32,slight:61,slightli:[2,6,25,27,69],slip:[21,69],slope:[4,69],slot:54,slow:[31,46,61,69,227,242],slower:[31,32,46,69],slowest:32,slowli:[24,32],small:[8,18,20,21,31,44,51,62,63,69,110,240],smaller:[29,32,63,67],smallest:69,smarter:17,smi:61,smooth:[2,19],smoother:32,smoothli:[2,27,32],snapshot:[13,17,18,19,32,41,45,46,67,69,99,100,102,109,118,213,214,216,244],snapshot_:214,snapshot_id:69,snippet:[4,22,40,48,188],snow:21,soccer:41,socket:44,soft:[53,67,69],softwar:[0,9,24,25,28,30,39,40,44,45,47,49,50,52,65,67,69,78,82,96,134,172,174,175,176,177,178,179,180,181,182,183,184,185,190,201,205,206,217,220,224,227,228,229,230,231,238,242],software_correct:69,software_releas:[24,66,69],softwarevers:134,solder:50,sole:66,solid:[69,227],solut:[9,49,65,66,69,235],solv:[67,69,213],some:[2,6,7,8,14,17,19,22,24,25,27,29,31,32,39,40,42,44,45,47,49,59,60,61,62,63,64,65,66,67,69,77,88,109,118,169,170,172,182,213,215,225,231],some_image_channel:35,somehow:69,someon:14,someth:[6,16,22,31,32,60,62,65,67,69,92,93,98,113,115,225],sometim:[6,27,29,65,67,69,227],somewher:[6,32,100],song:[2,29,32],soon:[33,69],sooner:[67,69],sort:[25,29,31,44,67,69,100],sound:[67,125],soundrequest:69,sourc:[7,19,35,36,37,45,46,60,61,62,63,65,66,67,69,72,73,74,76,77,78,79,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,164,165,166,167,168,169,178,184,205,207,208,213,221,227,231,236,240,242],source1:[46,208],source2:[46,208],source_fil:188,source_nam:102,sourcedataerror:[101,114],sourcenam:101,space:[6,7,17,20,21,27,28,29,31,37,41,50,52,54,58,63,67,69,119,132,172,173,174,178,184,204,213,216,220,234,241],spacebar:220,spam:40,span:[20,25],spars:[27,28,46,68,69,215],spawn:24,speak:[0,44,66],speaker:[67,69],spec:[25,69,136,143,148,149,162],speci:[24,66,69],special:[7,20,26,30,37,39,41,52,67,69],special_data:46,special_fram:41,specialfram:41,specif:[2,5,11,13,17,19,26,28,29,31,32,36,37,38,40,41,42,44,45,46,47,48,49,55,58,61,65,66,67,69,72,73,74,81,82,84,95,100,102,104,108,110,111,119,121,122,140,171,190,192,198,212,213,214,217,222,228,236,237,242],specifc:69,specifi:[2,6,7,16,17,20,22,24,25,27,28,29,31,32,36,37,39,44,45,46,49,56,58,63,66,69,73,75,81,82,83,86,88,89,92,93,96,98,100,101,102,104,106,107,114,119,121,125,129,132,136,139,143,151,152,154,157,163,164,173,178,184,189,190,191,192,205,206,208,210,214,221,224,225,227,231,233,234,236,240,242,245],speed:[2,4,8,13,15,16,21,22,27,31,32,46,49,64,67,69,100,117,205],speed_multipli:[2,32],speed_vari:[2,32],spell:65,spend:[27,32,60],spent:[2,32],sphere:[69,240],spheric:69,spike:53,spin:[123,139],spiral:32,splash:50,split:[2,32,42,61,63,67,69,84,148],split_dataset:61,split_fract:[2,32],sport:231,spot:[1,7,9,10,11,12,14,17,19,20,21,22,24,25,28,29,30,31,32,33,35,36,37,38,40,43,44,45,48,50,51,52,53,57,58,59,61,62,63,64,68,74,75,82,95,113,118,119,125,126,127,128,129,130,131,132,133,134,170,173,186,187,189,190,195,197,198,200,202,203,204,205,206,208,212,214,218,220,221,222,223,224,225,226,229,232,234,236,237,238,239,240,241,242,243,244,245],spot_cam:[67,75,125,126,127,128,129,130,131,132,133,134],spot_check:135,spot_check_cli:135,spot_check_command:135,spot_check_command_async:135,spot_check_feedback:135,spot_check_feedback_async:135,spot_check_servic:75,spot_command_pb2:119,spot_detect_and_follow:231,spot_ip:224,spot_light:232,spot_tensorflow_detector:[231,233],spot_v2_0:66,spotcam:[35,69,75],spotcaml:67,spotcamresetautofocu:67,spotcamstoremedia:22,spotcheck:[37,67,69,135],spotcheckcameratimeouterror:135,spotcheckcli:135,spotcheckcommand:69,spotcheckcommandrespons:67,spotcheckendstoptimeouterror:135,spotcheckerror:135,spotcheckfeedback:69,spotcheckfeedbackrespons:[67,135],spotcheckgroundcheckerror:135,spotcheckimucheckerror:135,spotcheckloadcelltimeouterror:135,spotchecknotsittingerror:135,spotcheckpoweronfailur:135,spotcheckresponseerror:135,spotcheckservic:[67,135],spotcheckstandfailureerror:135,spotchecktimedouterror:135,spotcheckunexpectedpowerchangeerror:135,spotcor:[47,49,55,231],spotdock:69,spotfetchcli:63,spotti:44,sprawl:[29,31,32],spread:[2,32],spun:38,squar:[17,20,69,172,216,240],squeez:[2,7,27,69],squeeze_grasp:69,squeeze_grasp_disallow:69,src:[66,242],ssd:[61,231],ssd_resnet50_v1_fpn_640x640_coco17_tpu:[61,64],ssh:[49,55,56,227],ssid:[69,227],ssl:[69,122,130],stabil:[65,67],stabl:[41,56],stack:[3,24,44],staff:69,stage:[7,69],stai:[8,18,19,41,52,64,69],stair:[4,13,17,19,45,52,67,68,119,171,245],stair_hint:[69,119],staircas:[17,19,69,231],stairs_tform_landing_cent:69,stairtrack:69,stairwai:4,stairwel:21,stale:[67,69,104],stall:[58,66,104,119],stamp:[32,67,212],stanc:[2,27,32,67,119,203],stance_command:119,stance_feedback:69,stance_in_plac:234,stance_length:[2,32],stance_request:69,stance_width:[2,32],stand:[2,4,6,15,22,24,27,29,31,32,33,38,45,52,53,62,63,64,66,67,69,119,135,171,175,176,179,180,181,182,183,195,205,214,217,220,225,232,234,241,245],stand_command:[67,119],stand_feedback:69,stand_miss:22,stand_request:[22,69],stand_tim:[2,32],standalon:56,standard:[1,4,10,22,27,32,38,44,46,47,48,50,55,56,60,63,66,67,68,227,229,242],standbi:4,standcommand:22,stare:69,start:[2,11,13,16,17,18,20,21,22,24,25,27,28,29,30,31,32,33,35,36,37,40,44,46,49,53,59,61,62,63,64,65,66,67,69,74,79,81,86,90,93,100,102,113,117,118,119,123,129,135,136,139,143,144,170,171,186,190,192,213,214,220,224,226,227,231,232,233,236,245],start_async:79,start_captur:102,start_counter_state_nam:69,start_datetim:136,start_fail_when_not_on_rout:69,start_goto_rout:69,start_goto_start:69,start_level:169,start_nsec:[81,136],start_record:117,start_recording_async:117,start_recording_st:74,start_recording_state_async:74,start_serv:222,start_slic:2,start_tim:[2,63,69,81],start_time_handl:72,start_time_sec:86,start_time_sync:118,start_timestamp:69,start_timestamp_sec:[90,118],start_unknown:69,starter:67,starting_angl:[2,32],starting_veloc:69,startrecord:[17,18,69,74],startrecordingrespons:67,startrecordingst:[2,28,31],startrecordingstaterequest:74,startrecordingstaterespons:74,startup:[24,36,67,69],stat:[29,68,69],state:[2,4,6,13,18,19,21,22,24,28,29,31,40,42,44,46,48,61,63,64,68,74,75,83,88,93,94,97,98,100,102,104,113,115,117,118,119,129,131,136,164,170,197,198,202,204,219,227,240],state_body_pos:69,state_camera_check:69,state_cli:[66,115,121],state_descript:69,state_endstop_c:69,state_error:69,state_estop:69,state_finish:69,state_hip_range_of_motion_check:69,state_loadcell_c:69,state_nam:[22,69],state_not_estop:69,state_off:69,state_off_shore_pow:69,state_on:69,state_on_shore_pow:69,state_powering_off:69,state_powering_on:69,state_reverting_c:69,state_start:69,state_unknown:69,state_unknown_shore_pow:69,state_user_abort:69,state_waiting_for_command:69,statement:[49,67,69,123],static_ip:227,station:[15,22,67,69,94],station_id:94,stationari:[32,205],statist:29,statu:[15,18,19,21,22,28,31,36,37,39,40,42,44,45,46,55,56,58,62,63,66,67,74,83,84,85,86,87,88,93,94,96,97,100,104,106,115,117,118,119,123,129,135,204,213,225,227,230,236,242,245],status:[58,67,69,88,102],status_abort:69,status_acquir:69,status_acquisition_cancel:69,status_already_answ:69,status_already_exist:69,status_ambigu:69,status_animation_validation_fail:2,status_applying_forc:69,status_arm_is_stuck:69,status_at_go:[63,69],status_at_prep_pos:[15,67,69],status_battery_miss:69,status_behavior_fault:[67,69],status_boot:69,status_calibration_error:69,status_camera_focus_error:69,status_camera_not_detect:69,status_cancel_acquisition_fail:69,status_cancel_in_progress:69,status_charg:69,status_clear:[69,119],status_command_in_progress:69,status_command_overridden:[16,69],status_command_timed_out:[16,69],status_compile_error:69,status_complet:69,status_config_mismatch:69,status_constraint_fault:[16,21,69],status_constraint_viol:69,status_could_not_create_waypoint:69,status_could_not_update_rout:69,status_data_error:69,status_data_invalid:69,status_data_unavail:69,status_delet:69,status_deletion_fail:69,status_discharg:69,status_dock:[15,69],status_does_not_exist:69,status_drag:69,status_endpoint_mismatch:69,status_endpoint_unknown:69,status_error:69,status_error_command_timed_out:69,status_error_dock_lost:69,status_error_dock_not_found:69,status_error_gripper_holding_item:[67,69],status_error_leas:69,status_error_no_timesync:69,status_error_not_avail:[67,69],status_error_not_dock:[67,69],status_error_system:[67,69],status_error_too_dist:69,status_estop:69,status_exist:69,status_expir:69,status_expired_application_token:[44,69],status_extrinsic_write_fail:69,status_fail:69,status_failed_to_cancel:69,status_failur:69,status_fault:69,status_fault_already_act:69,status_fault_not_act:69,status_feature_desert:69,status_fiducial_pose_not_ok:69,status_fiducial_pose_uncertain:69,status_fiducial_too_far_awai:69,status_fiducial_too_old:69,status_following_rout:[16,69],status_getstatus_error:69,status_going_to_go:69,status_going_to_st:69,status_grasp_fail:69,status_grasp_is_lost:69,status_high_error:69,status_image_data_error:[67,69],status_in_progress:[15,69],status_incompatible_hardwar:69,status_incomplete_data:2,status_incorrect_challenge_respons:69,status_internal_error:[46,69],status_intrinsic_write_fail:69,status_invalid:96,status_invalid_application_token:[44,69],status_invalid_cod:69,status_invalid_credenti:69,status_invalid_edg:69,status_invalid_endpoint:69,status_invalid_graph:[67,69],status_invalid_hint:69,status_invalid_id:69,status_invalid_leas:69,status_invalid_login:[44,69],status_invalid_mutation_id:69,status_invalid_param:69,status_invalid_pos:69,status_invalid_question_id:69,status_invalid_request:[67,69],status_invalid_resourc:69,status_invalid_session_id:69,status_invalid_token:[44,69],status_invalid_uploaded_choreographi:2,status_is_sit:69,status_is_stand:69,status_lease_error:[2,16,69],status_license_error:69,status_lost:[16,21,69],status_malform:69,status_map_modified_during_process:69,status_map_too_large_licens:69,status_max_iter:69,status_max_tim:69,status_misalign:69,status_miss:69,status_missing_fiduci:[67,69],status_missing_input:69,status_missing_leas:69,status_missing_transform:69,status_missing_waypoint_snapshot:69,status_more_samples_need:69,status_motors_on:[67,69],status_near_go:[67,69],status_never_run:69,status_no_anchor:69,status_no_licens:69,status_no_loc:[16,69],status_no_matching_fiduci:69,status_no_miss:69,status_no_mission_plai:69,status_no_path:[67,69],status_no_permiss:69,status_no_recorded_inform:2,status_no_rout:[16,69],status_no_such_grid:69,status_no_timesync:69,status_non:69,status_nonexistent_servic:69,status_not_active_leas:69,status_not_authoritative_servic:69,status_not_clear:69,status_not_found:69,status_not_localized_to_end:69,status_not_localized_to_existing_map:69,status_not_localized_to_map:[67,69],status_not_localized_to_rout:[16,69],status_not_manag:104,status_not_powered_on:69,status_not_ready_yet:69,status_not_record:69,status_not_yet_valid:69,status_ok:[2,44,69],status_old:69,status_old_docking_command:69,status_optimization_failur:69,status_other_failur:69,status_other_own:104,status_overridden:[67,69],status_paus:69,status_payload_not_author:69,status_point_cloud_data_error:69,status_power_error:69,status_powered_off:69,status_process:[69,119],status_queu:67,status_reached_go:[16,69],status_record:69,status_recording_buffer_ful:2,status_remote_cloud_failure_no_data:69,status_remote_cloud_failure_not_in_directori:69,status_request_error:69,status_request_id_does_not_exist:69,status_resource_already_claim:69,status_revok:[69,104],status_robot_command_error:69,status_robot_command_issu:2,status_robot_fault:16,status_robot_frozen:[16,69],status_robot_impair:69,status_run:[67,69],status_sav:[46,69,88],status_self_own:104,status_serial_mismatch:69,status_service_fault:69,status_service_not_readi:69,status_shore_power_connect:69,status_small_mass:69,status_snapshot_does_not_exist:69,status_source_data_error:69,status_stalled_holding_item:69,status_stanc:69,status_stop:[67,69],status_stuck:[16,67,69],status_success:[69,225],status_system_fault:69,status_target_not_cent:69,status_target_not_gravity_align:69,status_target_not_in_view:69,status_target_upside_down:69,status_temporarily_locked_out:[24,44,69],status_timedout:[46,69],status_to_error:84,status_to_str:84,status_too_dist:69,status_too_far_awai:69,status_too_far_from_existing_map:[67,69],status_tool_trajectory_stal:69,status_trajectory_cancel:69,status_trajectory_complet:69,status_trajectory_stal:69,status_unclear:69,status_unknown:[2,44,58,69],status_unknown_camera:69,status_unknown_capture_typ:69,status_unknown_fram:69,status_unknown_recording_session_id:2,status_unknown_route_el:69,status_unknown_sourc:69,status_unknown_waypoint:69,status_unmanag:69,status_unown:104,status_unrecognized_command:[67,69],status_unsupport:69,status_unsupported_image_format_request:[67,69],status_user_cancel:69,status_valid:69,status_validate_error:69,status_warn:69,status_wrong_epoch:69,statuscod:66,statustyp:84,stdin:65,stdout:63,steadi:[32,69,232],steer:32,step:[2,4,17,27,28,29,31,39,45,46,49,52,55,56,59,61,64,66,69,166,221,228,240],step_param:2,step_position_stiff:[2,32],stereo:[4,67],stick:[6,22,177,245],sticker:65,stiff:69,stiffer:69,still:[2,6,14,16,27,33,40,42,46,48,55,61,63,64,66,67,69,93,104,107,113,228],stillimag:69,stitch:[67,69,202,227,230],stitch_front_imag:235,stock:29,stood:65,stop:[2,3,10,16,21,22,23,28,29,32,33,45,46,49,56,60,62,64,67,69,74,75,93,96,102,104,113,115,117,118,119,122,123,136,138,139,164,167,172,173,174,175,176,177,178,179,180,181,182,183,184,185,195,198,214,217,220,222,224,225,226,227,231,234,241,245],stop_async:[96,167],stop_captur:102,stop_command:119,stop_feedback:69,stop_level:[45,66,69,96],stop_level_detail:[66,69],stop_miss:164,stop_mission_async:164,stop_record:117,stop_recording_async:117,stop_recording_st:74,stop_recording_state_async:74,stop_request:69,stop_time_sync:118,stoplevel:96,stopmiss:[67,69],stoprecord:[18,69],stoprecordingst:[2,28,31],stoprecordingstaterespons:74,storag:[37,67,69,88,104,137,188,192],store:[2,13,14,17,18,19,23,25,28,34,35,36,37,41,45,46,48,60,63,67,68,69,74,75,86,87,88,94,102,129,146,148,149,151,153,154,158,159,166,187,188,192,213,216,228,230,236],store_async:129,store_cli:88,store_data:[46,88,89],store_data_async:89,store_help:[46,88],store_imag:[88,89],store_image_async:89,store_metadata:[46,88,89],store_metadata_async:89,store_retriev:230,store_tru:62,stored_pb_typ:166,storedata:69,storedatarequest:123,storedatarespons:89,storeimag:69,storeimagerequest:123,storeimagerespons:89,storemetadata:67,storemetadatarespons:89,stow:[6,63,64,67,69,196],stow_cmd:[63,64],stow_stat:69,stowabl:69,stowstate_deploi:69,stowstate_stow:69,stowstate_unknown:69,str:[2,62,69,123,213],strafe:[33,220,241],straight:[4,17,63,64,69,177],straight_staircas:69,straightstaircas:67,strategi:[14,44],stream:[2,3,17,18,25,31,37,44,67,74,84,100,124,126,131,133,139,142,143,164,208,227,242],stream_data_read:160,stream_file_index:160,stream_intermediate_result:[69,109,213],streamabl:25,streamdataread:160,streameddataread:143,streaming_common_header_error:84,streaming_common_lease_error:84,streamlin:[45,67,93],streamqual:68,streamqualitycli:133,streamqualityservic:[67,133],street:22,strength:[2,27,69],stretch:[2,27,29,32],strict:[67,69],stricter:10,strictli:[64,69],strictvers:120,strike:217,string:[2,22,24,25,36,41,44,46,49,58,66,67,69,73,74,78,81,84,85,86,88,89,90,99,100,101,102,104,106,113,114,117,118,119,121,122,123,129,136,148,149,156,159,162,166,169,190,224,225,227,228,242],string_messag:228,string_valu:69,stringifi:169,stringvalu:2,strip:[37,44,69],strip_get_image_respons:123,strip_image_respons:123,strip_large_bytes_field:123,strip_local_grid_respons:123,strip_log_annot:123,strip_record_data_blob:123,strip_record_signal_tick:123,strip_store_data_request:123,strip_store_image_request:123,strive:69,strong:44,strongli:84,struct:69,struct_typ:69,structur:[9,18,28,31,37,41,42,44,46,50,60,62,64,66,67,69,73,85,87,99,100,127,144,146],stub:[78,120],stub_creation_func:84,stuck:[14,16,18,67,69,100],studi:52,stuff:60,stun:69,style:[2,32,37,57,58,69,70,239],sub:[2,42,44,66,67,69,73,82,104],sub_leas:104,sub_resourc:69,subclass:[66,67,84,100,102,107,118],subcommand:[69,83],subdirectori:58,subfield:69,subfold:214,subgraph:[69,109],subject:69,subleas:[67,104],sublease_nam:[67,104],submessag:[25,69],subnet:69,subnet_mask:227,subpars:83,subsequ:[19,31,32,40,42,45,67,69,104,122,164],subset:[6,36,37,42,69,231,233],substr:67,subtl:213,subtract:[64,69],subtre:[22,42,69],succe:[14,21,22,27,31,63,67,69,97,102,213,225],succeed:[2,44,63,67,69,97,115,118],succeeed:69,success:[2,13,16,18,22,24,44,55,62,63,64,65,67,69,85,87,88,102,113,115,119,135,165,169,188,225],successexitstatu:56,successfulli:[2,19,22,31,32,45,60,63,64,65,66,67,69,86,88,102,111,119,136,173,177,208,227,236,242],suddenli:10,sudo:[30,49,55,56,189,190,221,222,224,231,239,242,245],suffici:[21,63,64,65,66,69],suffix:[58,101],suggest:[49,69,117,227,242],suit:20,summar:69,summari:[9,69],super_leas:104,superced:67,supervis:[10,66],supervisor:22,suppli:[7,24,53,65,69,100,119,214,224],support:[0,2,5,6,7,9,11,17,19,24,25,26,29,30,31,33,44,47,49,52,53,54,55,58,62,65,66,69,75,78,106,119,163,187,190,205,231,233,245],suppos:[2,14,213],suppress:[2,69,93],suppress_incorrect:96,sure:[17,20,29,46,49,58,60,62,63,64,65,66,67,69,88,171,172,174,175,176,177,178,179,180,181,182,183,184,185,217,220,227],surfac:[5,7,50,54,66,67,68,69,75,196,215],surround:[45,50,67],surviv:50,suspected_ambigu:69,sustain:50,swai:2,sway_param:2,sway_style_fast_out:2,sway_style_fast_return:2,sway_style_plateau:2,sway_style_spik:2,sway_style_squar:2,sway_style_standard:[2,32],sway_style_unknown:2,swing:[2,27,32,52,69],swing_direct:69,swing_direction_insw:69,swing_direction_outsw:69,swing_direction_unknown:69,swing_height:[2,32,69],swing_height_high:69,swing_height_low:69,swing_height_medium:69,swing_height_unknown:69,swing_slic:[2,32],swing_veloc:[2,32],swing_waypoint:[2,32],symbol:[44,69],symmetr:[69,110],symmetri:69,sync:[2,8,29,58,63,66,67,68,69,75,83,94,100,118,122,198,220,237,241],sync_with_directori:[60,67,118],synchro:[67,119],synchro_se2_trajectory_command:119,synchro_se2_trajectory_point_command:[67,119],synchro_sit_command:[67,119],synchro_stand_command:[66,67,119],synchro_trajectory_command_in_body_fram:119,synchro_velocity_command:[67,119],synchron:[2,5,24,26,30,32,45,52,53,64,67,68,69,97,107,119,136],synchronized_feedback:69,synchronizedcommand:[67,119],syntax:[69,99,187],synthet:69,sys:[60,62,63],system:[2,6,10,15,17,19,21,22,24,28,32,37,42,44,45,46,47,48,49,50,52,53,56,58,61,62,63,66,67,69,79,93,96,97,100,101,104,107,114,119,123,127,129,136,139,146,162,202,204,205,213,216,228,229,231,239,242],system_fault:69,system_fault_st:69,system_stat:129,systemctl:[55,56],systemd:[56,62],systemfault:67,systemfaultst:45,tab:[27,29,33,49,56,61,220,223,224,241,242],tabl:[29,33,55,69,75,163,170],tablet:[3,9,10,13,14,18,19,21,23,28,36,40,42,43,45,46,47,48,55,60,62,63,64,66,67,69,171,173,190,195,208,214,220,225,226,227,231,234,235,236,238,242],tag36h11:20,tag:[15,35,41,69,125,129,205,213,216],tag_async:129,tag_id:69,take:[2,6,10,16,18,20,21,22,24,27,29,31,32,36,39,40,42,44,45,46,49,55,56,58,60,61,62,63,64,67,69,83,84,85,94,104,107,119,162,173,178,184,188,190,213,221,225,227,229,231,233,235,240,241,242],take_async:104,takeleas:[42,69,104],taken:[6,17,18,23,24,46,50,63,65,66,69,77,104,216],talk:[39,44,60,67,69,84,172,175,176,178,179,180,181,182,183,184,217,225],tall:[51,69,217],tangential_spe:[69,119],tap:32,tape:[20,177],tar:[49,55,61,190,222,224],target:[6,20,48,50,56,58,62,64,65,67,69,96,117,205,224,225],target_bitr:133,target_config_id:[69,96],target_endpoint:[39,69],target_nam:166,target_pb_typ:166,target_trajectory_in_frame1:69,target_trajectory_initial_veloc:69,targetbitr:69,task:[6,7,12,22,42,55,67,69,75,108,173,213],task_t_tool:69,task_typ:[69,119],task_type_hold_pos:69,task_type_r3_circle_extradof_forc:69,task_type_r3_circle_forc:69,task_type_r3_linear_forc:69,task_type_se3_circle_force_torqu:69,task_type_se3_rotational_torqu:69,task_type_unknown:69,tbd:69,tcp:48,teach:64,team:55,teardown_sess:167,teardown_session_async:167,teardownsess:[22,69,225],technic:9,techniqu:[20,58,63,119],teleop:[35,36,46,69,227],teleop_2020:35,teleop_:35,teleoper:35,tell:[2,10,17,18,22,61,63,64,67,69,100,119,122,144,213,223,228],telop:69,temp:[8,69,129],temperatur:[4,67,126,127,129],tempo:32,temporari:67,temporarili:[44,67,69,78],temporarilylockedouterror:78,tempt:88,tenni:43,tensor:[62,69],tensorboard:61,tensorflow:[61,62,64,67,170,202,221,222,231],tensorflow_object_detect:231,tensorflow_serv:[62,221],tensorflowobjectdetectionmodel:62,term:[26,44,58,66,69],termin:[28,46,56,61,62,63,65,66,97,214,220,225,241],terminolog:238,terrain:[17,45,67,69,117,216,240],terrain_param:69,terrainparam:67,test:[29,44,55,58,60,61,62,63,64,67,69,82,104,169,177,191,198,199,208,225,231,242,245],test_active_leas:[67,104],test_driv:227,test_payload:48,test_util:169,tester:[46,67,190,199,227,242],tester_program:[46,67],text:[2,25,27,28,29,31,48,58,62,64,66,69,73,83,90,107,118,192,218,220,225,238,240],text_messag:[69,90,107],textmessag:90,textmsg:[67,83],textmsgcommand:83,textual:2,textur:19,tf1_detection_zoo:221,tf2:61,tf_force_gpu_allow_growth:61,tform:[41,67,69,110],than:[0,2,6,10,17,21,22,24,27,29,31,32,35,39,42,44,45,46,54,55,56,58,59,61,63,65,66,67,69,78,88,97,104,113,119,208,220,227,240],thank:67,thei:[2,3,6,10,13,15,17,19,20,22,24,27,28,29,31,32,33,36,37,39,40,41,42,43,44,45,46,48,61,64,65,66,67,69,72,99,104,110,171,188,190,208,214,216,228,242],them:[2,13,17,20,22,28,29,33,35,40,45,46,49,58,61,65,66,67,69,83,88,92,100,156,187,213,214,233,235,239],theme:239,themselv:[10,39,42,48,61,67,228],therefor:[9,28,46,53,66,69,213,242],thermal:67,thermomet:[69,127],theta:[38,46,49,67,69,110,199,202,213,236],theta_ssid:227,thetayl00196843:227,thi:[0,2,4,6,7,8,9,10,11,14,15,16,17,18,19,20,21,22,24,25,27,28,29,31,32,33,34,35,36,37,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,55,56,58,59,60,61,62,63,64,65,66,67,69,70,73,74,77,82,83,84,85,87,88,89,90,93,94,96,100,101,102,104,107,109,110,112,113,114,115,117,118,119,122,123,126,130,135,136,139,140,148,149,151,152,154,155,156,157,159,162,164,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,204,205,206,207,208,209,210,211,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,231,232,233,234,235,236,237,238,239,240,241,242,244,245],thick:29,thin:58,thing:[6,10,42,46,60,61,62,63,69,104,110],think:[24,63,69],third:[38,40,55,65,66,116],this_model:62,those:[3,6,14,19,24,27,29,31,32,33,35,36,44,60,63,65,67,69,88,213,231,233,245],though:[14,17,67,166],thought:22,thread:[24,62,66,67,88,93,96,102,104,107,113,118,122,123,136,139,227,231],thread_except:136,thread_input_queu:62,thread_output_queu:62,threadpoolexecutor:[62,88],three:[6,7,25,27,29,31,36,44,64,69,119,213,231,233],threshold:[10,22,37,69,100,231,233],threshold_radian:[63,69],threw:[60,65,111],through:[6,7,10,14,17,19,20,25,27,28,32,33,36,37,38,40,44,45,46,49,53,55,56,62,64,65,67,69,87,88,93,97,98,100,123,136,139,170,173,174,190,202,213,215,217,223,227,228,236,239,242,245],throughout:[24,31,42,44,45,67,69],throughput:69,thrown:[31,67,88,118],thu:[25,69],thumb:60,thunk:148,thursdai:37,tick:[13,22,67,69,90,167],tick_async:167,tick_count:69,tick_data:69,tick_start_timestamp:69,tick_statu:167,tidi:107,tied:[50,69],tier:[2,32],tier_vari:[2,32],tight:6,tightli:[7,28,50],tile:69,till:69,tilt:[22,69,132,232],time:[2,4,6,10,13,15,17,18,19,20,21,22,25,27,29,31,32,33,36,37,38,40,41,42,44,45,46,48,49,52,53,55,58,60,61,62,63,64,65,66,67,68,69,74,75,77,81,83,86,88,90,94,96,97,100,101,102,104,107,109,114,115,118,119,121,122,123,135,140,162,164,171,172,173,190,198,208,212,213,216,220,224,227,228,230,231,233,237,240,241,245],time_format_desc:162,time_nsec:162,time_rang:91,time_remaining_nam:69,time_sec:118,time_since_refer:69,time_since_reference_sec:119,time_since_valid_respons:[66,69],time_spec:136,time_start:63,time_start_point:[74,140],time_sync:[24,60,63,66,118,136],time_sync_cli:[136,237],time_sync_endpoint:[107,136],time_sync_interval_sec:[118,136],time_sync_servic:75,time_sync_service_not_ready_interval_sec:136,time_to_go:69,timebas:69,timed_out:69,timedouterror:[97,136],timeerror:100,timefram:74,timelin:[29,31,37,69],timeout:[10,16,36,39,40,42,44,45,46,63,66,67,69,88,93,94,96,100,102,107,113,118,119,121,136,228,242],timeout_child:69,timeout_deadlin:[46,69],timeout_sec:[63,64,66,102,115,118,119,123,135,136,139],timeout_second:69,timer:69,timerang:136,timespan:[69,81,136,162,187,192],timespan_spec:[81,136,162],timespec_to_robot_timespan:136,timestamp:[2,24,25,27,28,29,31,35,36,37,41,44,45,58,66,67,69,74,85,87,90,100,102,107,116,118,136,138,140,148,149,156,159,162,190,192,236],timestamp_cli:69,timestamp_filt:69,timestamp_nsec:[25,147,148,149,151,154,155,156,157,158,159],timestamp_proto:162,timestamp_sec:118,timestamp_str:162,timestamp_to_datetim:162,timestamp_to_nsec:162,timestamp_to_sec:162,timesync:[58,67,69,74,86,94,100,119,136,140,164],timesync_endpoint:[74,100,119,140,164],timesync_timeout_sec:136,timesynccli:136,timesynccommand:83,timesyncendpoint:[107,136,237],timesyncerror:136,timesyncrequir:[67,97],timesyncrespons:136,timesyncroundtrip:136,timesyncservic:136,timesyncthread:[24,136],timesyncupd:[24,69],timezon:190,tip:[7,8,10,60,61,65,69],tippi:32,titl:[22,29],tls1:44,to_adjoint_matrix:110,to_axis_angl:110,to_euler_zxi:161,to_matrix:110,to_nsec:37,to_obj:110,to_pitch:110,to_proto:[96,110],to_quaternion:161,to_rol:110,to_rot_matrix:110,to_sec:37,to_timestamp:69,to_vector:110,to_waypoint:[69,117],to_waypoint_id:117,to_yaw:[64,110],todai:19,tof:8,togeth:[18,32,36,41,53,64,67,69,213,227],toggl:[29,67,205],toi:[60,61,62,63,64],token:[24,37,44,48,58,62,66,67,69,75,78,82,86,93,97,113,118,122,190,228],token_cach:[118,137],token_cb:82,token_manag:138,tokencach:137,tokencacheerror:137,tokencachefilesystem:137,tokenmanag:138,toler:[63,69,100,213],tolerance_default:69,tolerance_ignore_poor_feature_qu:69,tolerance_unknown:69,tolerance_z:69,tolist:62,too:[6,18,21,29,32,44,45,60,64,66,67,69,100,117,119],toodistanterror:[100,119],toofarfromexistingmaperror:117,took:[42,67,69,119],tool:[2,6,17,25,28,29,37,46,47,49,69,73,200,214,227,228,242],tool_trajectory_in_frame2:69,toolkit:216,toomanyrequestserror:[67,97],top:[3,6,7,15,20,21,29,32,41,44,49,51,54,55,60,63,65,67,69,213,227,231,233,236,242],top_color:[2,32],top_i:62,top_land:69,top_left:[2,32],top_right:[2,32],topmost:32,topolog:[9,13,18,69,100,109,213],torn:69,torqu:[6,69,173],torque_i:119,torque_limit:[69,119],torque_x:119,torque_z:119,total:[17,31,52,53,69,147,213,233],total_byt:[69,147],total_delai:233,total_mass:69,total_s:69,totimedelta:162,totla:69,touch:[2,6,8,32,69],touch_offset:32,touchdown:[2,27,31,32],touchdown_veloc:32,tow:67,toward:[7,17,32,43,67,69,136,205,231],towel:60,tower:15,tpp:53,traceback:[65,66,84],track1:27,track:[2,19,28,29,32,40,45,58,61,67,69,73,74,88,102,104,136,192],track_hand_rt_body_opt:72,track_hand_rt_feet_opt:72,track_n:27,track_swing_trajectori:[2,27],track_swing_trajectories_opt:72,traction:[31,32],tradit:[190,225,227,242],traffic:[48,55,221],train:[43,60,62,64,66,220,231,233],train_input_read:61,traine:66,trained_checkpoint_dir:62,trainer:66,trait:69,trajectori:[2,6,7,27,28,32,42,63,66,67,68,119,170,172,175,179,182,196,203],trajectory_command:[63,64,67,119],transfer:[9,24,60,61,64,137,215,216],transform:[6,13,17,19,45,46,66,67,69,99,102,110,117,119,205,206,213,215,216,244],transform_cloud:110,transform_cloud_from_matrix:110,transform_point:[64,110],transform_se2veloc:110,transform_se3veloc:110,transform_vec3:[67,110],transforms_snapshot:[41,63,64,69],transforms_snapshot_for_camera:[63,69],transforms_snapshot_manipulation_data:69,transient_failur:97,transientfailureerror:[67,97],transit:[2,27,29,31,52,69,70,88,119,204],transition_state_kneel:2,transition_state_sit:2,transition_state_sprawl:2,transition_state_stand:2,transition_state_unknown:2,translat:[2,32,33,41,66,69,82,97,110,130],translate_except:82,translation_is_absolut:[2,32],translation_multipli:[2,27],transmit:[45,69],transpar:69,transport:[44,58,66,67],transpos:64,trap:10,travel:[6,10,14,17,32,67,69,100,121,214],travel_param:[69,100,169],travelparam:[14,67,100],travers:[7,10,11,17,18,19,21,69,117],treat:[20,24,58,67,69,104],tree:[13,41,42,45,67,69,99,118,166,169,244],tree_status_from_tick_statu:167,tree_to_str:169,trend:69,tri:[14,18,67,69,96,242],trial:31,triangl:213,trigger:[2,10,11,18,19,22,36,38,48,58,66,67,69,85,87,88,89,97,98,100,102,204,225,229],trigger_fault:102,trigger_service_fault:98,trigger_service_fault_async:98,triggerd:69,triggerservicefault:69,triggerservicefaultrespons:98,trip:[69,136,237],trivial:66,trot:[67,69],troubl:[16,100,230],troubleshoot:[49,61,62,63],trueclass:[2,69],truncat:[2,27,60,74,162],truncatable_opt:72,trunk:[65,66],trust:69,trustworthi:61,truth:[45,69],tryioctl:242,tuck:6,tunabl:45,tune:[10,64,66,67,177],tunnel:56,tup:169,tupl:[62,74,82,84,101,102,104,110,127,169],turn:[2,10,21,32,33,39,45,55,56,65,67,69,74,88,131,171,220,241,245],turn_param:2,tutori:[56,58,59,61,62,64,66,67,221],tweak:66,twerk:2,twerk_param:2,twice:[17,21,64],twilight:69,twin:[213,215],twist:69,two:[2,15,16,17,18,19,22,24,27,28,29,31,32,35,38,39,41,44,46,47,48,49,52,53,55,60,63,64,65,66,67,69,99,104,110,117,127,130,175,187,188,190,204,205,213,214,216,227,231,233,235,242],tx_bits_per_second:69,tx_byte:69,tx_fail:69,tx_packet:69,tx_power_dbm:69,tx_retri:69,txt:[2,28,65,67,171,172,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,204,205,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,223,224,225,226,227,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245],tying:63,type:[1,4,7,13,15,16,17,20,24,25,28,29,31,36,38,40,41,42,44,45,46,48,49,50,55,56,58,62,63,65,66,67,68,73,74,81,82,83,85,86,87,88,90,94,99,100,101,102,106,107,110,111,117,118,121,122,123,129,135,140,145,148,149,150,151,152,154,159,160,162,164,166,169,173,187,190,191,213,214,228,242],type_bool:69,type_enum:148,type_float32:69,type_float64:69,type_float:[22,69],type_hardwar:69,type_id:[37,69,90,107],type_int16:69,type_int32:69,type_int64:69,type_int8:69,type_int:69,type_messag:69,type_nam:[69,148,152],type_softwar:69,type_str:69,type_uint16:69,type_uint32:69,type_uint64:69,type_uint8:69,type_unknown:69,type_unspecifi:69,type_url:66,typedmessagechannel:145,typic:[2,4,9,18,20,21,24,37,41,42,44,46,52,66,69,84,90,100,104,122,192,242],ubuntu:[30,55,56,59,60,65,67,231,233,245],udp:[48,189],ufw:[62,190],uid:69,uint16:69,uint32:[2,69],uint32valu:69,uint64:[2,69],uint64_t:69,uint8:[60,62,63],uint:[2,69],uint_valu:69,ulong:[2,69],ultim:[24,29,46,227],unabl:[17,45,46,69,74,85,87,94,97,100,111,242],unabletoconnecttoroboterror:[67,97],unabletoloadapptokenerror:122,unachiev:[69,100],unambigu:214,unansw:69,unauthenticatederror:97,unauthor:228,unavail:[65,69,97,118],unawar:[13,20,69],unbox:65,uncertain:69,uncertainti:69,uncheck:[14,29,67],unclear:119,uncom:[64,224],uncompl:62,uncompress:[45,69],unconstrain:69,uncorrupt:69,undefin:44,under:[6,10,15,29,49,55,61,67,69,123,148,171,224],underli:[66,67,69,96,100,151,154],underpin:66,underscor:27,understand:[21,24,31,37,41,44,58,59,63,64,65,69,126,197,227,228],understood:69,underwai:69,undock:[67,69,94],unevenli:20,unexpect:[10,14,40,45,69,97,104,190,225,227,242],unexpectedli:46,unfilt:69,unfortun:55,unhealthi:69,unicast:69,unicod:[2,67,69],unifi:[25,69],unimpl:225,unimplementederror:[69,97],uniniti:[69,100],uninstal:[61,65],unintend:53,unintention:65,uniqu:[2,17,20,24,25,31,36,38,41,45,46,48,49,65,69,74,85,87,90,93,101,113,118,122,136,146,190,214,216,220,224,227,228,242],unique_id:[66,69,96,118],unit:[6,17,25,45,48,56,58,64,67,69,104,131,192],unitless:69,univers:20,unix:[56,118,136,148,156,159,162],unknown:[2,20,24,40,58,60,69,74,85,90,97,100,104,110,117,118,164,166,169],unknown_edge_snapshot_id:69,unknown_waypoint_snapshot_id:69,unknowncapturetypeerror:85,unknowndnsnameerror:[67,97],unknownframeerror:[67,119],unknownimagesourceerror:101,unknownmapinformationerror:100,unknownpointcloudsourceerror:114,unknownrecordingsessionid:74,unknownrouteelementserror:100,unknowntyp:166,unknownwaypointerror:[100,117],unkownrouteelementserror:100,unless:[2,15,16,18,20,29,32,35,49,66,69,117,222,224],unlik:[24,30,31,40,65,69,164,244],unlimit:[67,69],unload:69,unmanag:69,unmanagedresourceerror:104,unmodifi:172,unnecessari:40,unobstruct:15,unoptim:213,unorgan:69,unown:104,unpack:[60,62,63,69],unpopul:69,unpow:[21,50],unreach:[55,65,97,190,225,227,242],unread:214,unrecogn:[69,100],unrecongizedcommanderror:100,unrecover:[69,97],unregist:[62,67,69,83,93,97],unregisteredserviceerror:[67,118],unregisteredservicenameerror:[67,118],unregisteredservicetypeerror:[67,118],unregisterservic:[48,69],unregul:53,unrel:[46,214],unreli:[20,31,32,69],unsaf:69,unselect:29,unset:[2,6,20,44,58,67,69,84,119,136],unsetapptokenerror:122,unsetstatuserror:[97,101,114],unsign:[25,69],unspecifi:[2,27,37,60,69],unstitch:67,unstow:[6,67,196],unsuccess:[44,69],unsupervis:10,unsupport:[62,69],unsupportederror:119,unsupportedimageformatrequestederror:101,unsur:60,untar:231,until:[2,10,22,25,28,29,31,37,38,46,60,62,63,64,65,66,67,69,83,86,88,104,115,118,119,122,123,135,136,139,162,187,192,205,211,220,228],unus:69,unwil:69,unzip:[61,65],upcom:67,updat:[2,13,19,24,31,32,41,46,48,49,55,69,73,76,77,88,93,95,100,102,104,108,113,117,118,119,122,129,136,138,140,189,193,208,223,224,225,227,228,231,233,239,240],update_frequ:[115,118,119,135],update_from:[74,76,84,85,87,89,90,91,94,95,100,107,108,115,118,119,140,164],update_from_lease_use_result:104,update_payload_vers:113,update_payload_version_async:113,update_request_iter:84,update_response_iter:84,update_user_token:118,updated_vers:[69,113],updatepayload:69,updatepayloadattach:[67,69],updatepayloadvers:69,updateservic:[48,69],upgrad:[49,61,65,69,230],upload:[2,11,14,18,19,22,26,27,28,29,31,44,49,55,62,67,69,73,74,100,109,125,171,199,203,214,224],upload_animated_mov:74,upload_animated_move_async:74,upload_choreographed_sequ:[31,238],upload_choreographi:74,upload_choreography_async:74,upload_edge_snapshot:100,upload_graph:100,upload_graph_and_snapshot:213,upload_graph_async:100,upload_to_aw:188,upload_to_gcp:188,upload_waypoint_snapshot:100,uploadanim:27,uploadanimatedmov:[2,31,74],uploadanimatedmoverespons:74,uploadanimatemoverespons:74,uploadchoreographi:[2,31],uploadchoreographyrespons:74,uploadedgesnapshot:[17,18,69],uploadgraph:[18,67,69],uploadgrapherror:100,uploadgraphrequest:17,uploadwaypointsnapshot:[17,18,69],upon:[2,19,50,51,52,63,64,67,69],upper:[69,110,213],upper_limit:110,upper_mid_left:[2,32],upper_mid_right:[2,32],upper_tick_bound:164,uppermost:69,upright:[60,69,171,172,174,175,176,177,178,179,180,181,182,183,184,185,208,213,217],upsid:69,uptown:31,urdf:[45,52,67,69,83],url:[24,37,86],usabl:[25,67],usag:[24,25,37,39,204],usb:[17,22,33,46,55,67,69,215,216,227,242],use:[0,2,8,9,11,14,15,17,19,20,21,22,23,24,25,29,30,31,32,33,37,38,41,42,43,44,45,46,48,49,50,53,55,56,58,59,60,61,62,63,64,65,66,67,69,74,77,78,79,82,84,86,87,88,89,93,94,96,98,100,101,104,107,113,117,118,119,121,122,125,126,130,135,140,162,164,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,187,188,189,190,193,194,196,197,198,199,200,201,202,204,205,207,212,213,214,215,217,218,221,222,223,224,226,227,228,231,233,236,237,238,239,242,243,245],use_background_capture_thread:[46,102],use_display_nam:62,use_fiducial_id:[69,100],use_gyroscope_measur:69,use_kinematic_odometri:69,use_loop_closur:69,use_nano:162,use_visual_odometri:69,use_world_object:69,used:[0,2,6,7,11,12,13,15,17,18,19,20,22,23,24,25,26,27,28,29,31,32,33,35,36,37,38,40,41,42,44,45,46,48,49,52,55,56,58,59,60,61,62,63,64,65,66,67,69,72,73,74,82,84,85,86,87,88,90,91,93,96,100,101,102,104,107,110,113,117,118,119,131,135,136,139,143,148,149,152,172,173,177,190,191,196,198,203,206,208,213,214,215,216,220,224,225,227,228,229,231,233,236,237,238,242,244,245],useful:[6,7,19,29,32,37,40,41,43,44,45,63,66,67,69,93,104,223],usehernam:65,user:[2,6,7,10,13,16,17,20,24,25,30,31,33,36,37,40,44,45,46,48,49,55,56,60,61,62,63,64,66,67,69,74,78,86,87,88,90,93,97,107,113,117,118,119,135,136,138,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,190,192,193,194,195,204,205,206,207,208,209,210,211,212,213,214,217,218,219,220,221,223,224,225,226,227,228,229,234,236,237,238,239,240,241,242,243,244],user_data:69,user_nam:69,user_token:[69,82,118,137],user_token_requir:[69,93],usernam:[24,30,33,44,46,48,49,55,56,60,62,63,64,65,66,69,78,118,139,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,189,190,191,192,193,194,195,204,205,206,207,208,209,210,211,212,213,214,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,236,237,238,239,240,241,242,243,244,245],uses:[2,7,17,18,20,21,22,24,31,36,39,41,44,45,47,48,55,58,62,63,65,66,67,69,119,136,174,177,204,205,213,218,219,221,222,231,245],using:[0,2,4,6,7,9,11,13,16,17,18,19,21,22,24,25,27,28,29,32,33,36,37,41,42,44,45,46,48,49,53,55,56,59,60,61,62,63,64,65,66,67,69,74,77,78,84,86,96,97,99,100,101,104,106,110,114,118,119,123,151,154,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,187,188,189,191,192,193,194,198,204,205,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,223,225,226,228,229,231,232,233,235,236,237,238,239,240,241,244,245],usr:[56,65,205,231],usual:[2,16,24,31,35,49,50,62,69,118,184,231],utf:[2,25,69],util:[19,24,43,60,62,63,64,67,68,75,77,83,85,96,129,141,163,188,228],util_pb2:[22,169],uuid:[90,118,230],v4l2:242,v_rot:119,v_x:119,v_y:119,val:[72,118,136,162,187,192],valid:[2,16,24,27,31,37,39,41,42,44,45,46,48,55,66,67,69,74,78,86,88,99,104,107,115,117,118,119,136,137,148,159,164,166,167,228],validate_frame_tree_snapshot:99,validateframetreecycleerror:99,validateframetreedisjointerror:99,validateframetreeerror:[63,99],validateframetreeunknownframeerror:99,validationerror:[164,166],validationerrorreport:166,valu:[2,4,6,15,22,25,27,29,31,32,36,37,39,41,42,45,46,48,50,53,58,61,62,63,66,67,72,73,82,83,84,96,97,101,102,104,117,119,122,128,143,146,148,149,155,156,162,164,165,169,177,192,205,213,221,230,231,233,234,240],valuabl:10,value_from_respons:84,value_msg:169,valueerror:[84,85,87,143],valv:[69,173],vari:[2,21,31,32,37,40,69],variabl:[2,22,27,37,58,61,65,88,90,96,148,169,187,188,213,221,223,225,236],variabledeclar:[22,169],varianc:69,variant:67,variat:[2,32,69],varieti:[0,3,19,24,31,32,44,60,62,66,67,69],variou:[17,20,34,42,43,66,67,69,170,190,191,198,224],varnam:69,vec2:[63,64,110,119],vec2_proto:110,vec2valu:2,vec3:[41,63,64,73,100,110],vec3_proto:110,vec3trajectori:119,vec3valu:2,vector:[2,32,41,63,64,66,67,69,110,119,148,156,205,213],vector_alignment_with_toler:[63,69],vel:[67,110],vel_limit:[64,67,69,117,169],vel_of_a_in_b:99,veloc:[2,4,19,22,32,45,64,66,67,69,73,99,110,119,132,173],velocity_command:[67,119],velocity_in_frame_a:110,velocity_in_frame_b:110,velocity_in_frame_nam:69,velocity_limit:[69,100],velocity_of_a_in_c:99,velocity_of_body_in_odom:69,velocity_of_body_in_vis:[35,69],velocity_of_hand_in_odom:69,velocity_of_hand_in_vis:69,velodyn:[55,67,69,190,201],velodyne_servic:55,vendor:62,ventur:10,venv:[205,231,245],verbos:[63,64,65,66,135,139,219,236],veri:[10,17,21,28,31,36,44,52,55,58,66,67,69,177,205,213,227,228,232],verif:[44,122],verifi:[39,44,46,60,67,69,93,113,118,135,220,224,226,242],versa:24,versatil:47,version:[0,2,22,24,25,32,35,42,49,56,58,61,65,66,67,68,74,76,79,85,87,89,90,91,94,95,96,98,100,101,103,104,106,107,108,111,113,114,115,117,119,121,122,124,125,126,127,128,129,130,131,132,133,135,136,140,143,164,169,205,230,235],version_id:69,version_str:65,versioncli:134,versionservic:[67,134],versu:67,vert:63,vertex1:62,vertex2:62,vertex3:62,vertex4:62,vertex:[62,63,69,221],vertic:[2,4,20,29,31,32,41,69,126,175,213],via:[0,3,7,13,17,20,28,29,33,37,38,40,41,44,47,48,53,55,57,59,62,63,65,66,67,69,74,113,119,122,129,140,164,191,192,221,223,225,226,227,228,231,235],vice:24,video0:242,video1:242,video:[4,22,28,64,67,69,230,241,242],videoio:242,videoxxx:242,view:[0,2,3,4,7,11,29,31,40,43,46,47,49,56,57,59,60,62,63,67,69,126,170,197,205,215,216,220,226,240,242],view_map:[11,17,213,216],viewer:[17,43,62,63,64,214,215],viewpoint:[60,62],violat:[2,31,69],violated_object_constraint:69,violated_waypoint_constraint:69,virtual:[45,46,65,69,231,233,239,245],virtualenv:[60,61,62,205,231,233,239,245],visibl:[67,69,220,227,230],vision:[6,15,41,45,63,64,67,69,99,110,119,206,240],vision_frame_nam:[41,63,64,67],vision_tform_bodi:[41,69],vision_tform_dogtoi:63,vision_tform_flat_bodi:64,vision_tform_hand_at_drop:[63,64],vision_tform_left_camera:41,vision_tform_obj:63,vision_tform_odom:41,vision_tform_person:64,vision_tform_robot:64,vision_tform_sensor:69,vision_tform_soccer_bal:41,vision_tform_special_fram:41,vision_tform_target:64,visit:[14,22,227],visual:[2,19,20,21,41,45,60,61,64,67,69,101,102,199,202,206,213,214,215,216,233],visual_odometry_weight:69,visual_source1:46,visual_source2_not_thread:46,visual_surface_ground_penetration_mean:69,visual_surface_ground_penetration_std:69,visualimagesourc:[46,102],visualimagesource2:46,vlp:55,vnc:[47,55],vncconfig:56,vncpasswd:56,vncviewer64:56,volatil:69,volt:69,voltag:[4,53,69],volum:[29,125],voxel:240,vqfl7nrkvhhpouoo:214,vtk:[216,240],wai:[2,10,16,18,19,22,23,24,25,27,29,31,32,36,43,45,49,58,59,61,62,63,65,66,67,69,83,88,100,104,118,131,189,195,228],wait:[15,36,39,42,46,60,62,63,64,65,66,67,69,77,88,93,102,104,113,115,118,119,121,123,135,136,139,208,211,228],wait_for_stores_complet:88,wait_for_sync:[60,63,66,136],wait_heading_rt_vis:64,wait_position_rt_vis:64,wait_until_don:104,waiting_for_command:69,waitkei:63,walk:[6,7,10,15,17,22,33,43,45,50,60,62,63,64,67,69,119,172,174,178,185,196,205,213,214,215,216,217,228,231,245],walk_gaze_mod:69,walk_rt_vis:63,walk_to_object_in_imag:69,walk_to_object_ray_in_world:69,walkto_raycast_intersect:69,walktoobject:69,wall:[20,45,58,69,115,205,213],wallet:[74,84,85,87,89,90,91,94,100,104,107,115,140,164],want:[6,7,15,17,29,33,41,42,43,46,50,58,60,61,62,63,64,65,66,67,69,93,123,158,162,205,213,219,221,228,244],wantedbi:56,warm_fluoresc:69,warmstart:7,warmstart_command:69,warn:[2,6,31,37,40,45,46,62,67,69,74,102,104,192,205,227,228,236,242],wasd:[33,65,67,203,214,220,225,241],wasn:15,watch:[40,61,65,83],water:[8,50],wav:[69,125,230],wave:6,waypoint:[2,9,11,13,14,16,18,20,21,23,32,67,100,109,117,213,214,215,216,225],waypoint_anchor:69,waypoint_dwel:32,waypoint_env:117,waypoint_environ:69,waypoint_id:[69,100],waypoint_id_list:100,waypoint_nam:[69,117],waypoint_result:[69,213],waypoint_snapshot:[17,100,213,214,215,216],waypoint_snapshot_id:[69,100],waypoint_sourc:69,waypoint_source_alternate_route_find:69,waypoint_source_robot_path:69,waypoint_source_unknown:69,waypoint_source_user_request:69,waypoint_tform_bodi:69,waypoint_tform_ko:69,waypointregion:117,waypointsnapshot:[67,100],web:[24,38,46,48,49,55,67,113,118,188,191,199,202,208,227,228,236],web_cam_image_servic:242,webcam:67,webcamimageservic:38,webpag:[65,69,118,228],webrtc:[67,230],webserv:[69,223],websit:[20,69],wedg:104,weigh:52,weight:[4,8,32,60,61,109],weightless:[190,227],welcom:29,well:[2,6,10,16,22,24,25,26,28,29,31,35,36,37,39,40,41,44,45,46,49,58,61,62,64,66,67,69,70,73,74,85,87,99,201,208,210,214,226,227,228,231,236,237,240,242],went:[62,65,92,93,98,113,115],were:[17,31,32,35,41,42,44,58,61,67,69,79,85,87,88,109,117,213,229],weren:69,what:[0,2,15,24,27,29,32,36,40,42,44,46,48,49,55,58,60,61,62,63,64,65,66,67,69,88,90,100,111,119,170,213,216,228,232],whatev:[2,32,64,66,69,107],wheel:[29,30,67],when:[2,6,8,13,15,16,17,19,20,21,22,23,24,25,28,29,31,32,33,35,36,37,40,41,42,44,45,46,48,49,50,52,53,58,60,62,63,64,65,66,67,69,73,74,77,78,84,85,87,88,90,93,96,100,101,104,107,111,113,117,118,119,122,135,136,148,162,164,171,172,173,177,195,205,208,210,211,213,214,220,223,224,225,227,228,229,231,233,234,236,241,242],whenev:[17,28,56,69,214],where:[0,2,3,6,7,13,14,17,20,21,22,25,27,28,29,30,31,32,36,37,40,41,44,45,49,52,54,57,58,60,61,62,63,64,65,66,67,69,74,76,86,95,100,104,108,117,118,119,140,149,162,187,190,192,213,214,216,221,225,226,227,228,233,242],wherea:24,wherev:[60,65],whether:[2,10,14,17,21,22,24,29,31,32,41,42,45,58,67,69,82,90,91,100,103,104,110,119,213,228],which:[2,6,7,10,13,15,16,17,18,19,20,21,22,24,25,26,27,28,29,30,31,32,33,36,37,38,39,41,42,44,45,46,48,49,55,56,58,60,61,62,63,64,65,66,67,69,72,73,74,77,83,84,85,87,88,90,93,95,100,101,102,104,107,108,111,113,114,115,117,118,119,120,121,123,130,136,139,140,143,147,148,149,153,156,159,160,162,170,171,178,184,186,187,189,190,191,193,195,196,199,203,204,205,206,208,209,210,212,213,214,217,218,219,220,225,226,227,228,229,230,231,232,234,236,237,238,240,241,242,243,244,245],whichev:[29,32,42],white:[4,20,69,216,231],whithin:2,who:[0,42,44,50,55,69],whole:[19,25,29,37,66,69],wholli:28,whose:[69,97],why:[25,39,61,64,69,213],wide:[0,31,52,62,66,69],wider:[19,52,60],widest:58,widget:2,width:[2,4,32,60,69,242],wifi:[3,4,43,44,48,49,65,67,69,83,84,225,227],wifi_password:227,wifi_radio_power_st:69,wifi_radio_power_state_off:69,wifi_radio_power_state_on:69,wifi_radio_power_state_unknown:69,wifi_ssid:227,wifi_st:69,wiki:69,wikipedia:69,wil:228,willing:69,window:[20,29,30,56,59,60,63,65,67,188,205,208,231,233,235,239,242,245],wipe:[7,67,69],wire:[44,45,66,245],wirefram:69,wireless:[55,227],wirelessli:227,wise:69,wish:[0,21,29,32,33,44,69,213,223],within:[2,17,19,21,24,25,27,28,29,31,35,37,41,42,45,46,52,58,63,67,69,72,73,74,97,102,115,118,146,147,149,151,154,156,157,158,190,225,227,231,244,245],without:[6,7,10,14,15,17,19,20,21,25,27,28,30,31,39,42,44,47,48,49,50,52,55,56,65,66,67,69,78,86,88,93,118,119,122,160,189,192,214,220,221,228,236,244],withstand:[44,52],wolfram:69,won:[31,64,69,74],wonki:62,word:[27,58,104],work:[10,17,20,29,33,41,43,44,46,52,55,60,61,62,63,64,65,66,67,69,77,104,122,169,204,208,214,220,223,224,225,227,228,231,236,239,242,245],workaround:[44,67],worker:[62,67,69,111,190,221],workflow:42,workspac:[2,6,27,32,69],workspace_arm_move_param:2,workspace_dance_fram:27,world:[2,6,7,19,20,21,32,64,66,67,68,69,75,109,119,170,198,205,213,225,240],world_obj:140,world_obj_special_fram:41,world_object:[140,213],world_object_apriltag:69,world_object_cli:41,world_object_dock:69,world_object_draw:69,world_object_image_coordin:69,world_object_mut:67,world_object_pb2:140,world_object_result:[69,213],world_object_servic:75,world_object_unknown:69,world_object_weight:69,world_object_with_image_coordin:244,world_tform_fiduci:213,worldobject:[41,140],worldobjectcli:140,worldobjectservic:[67,140],worldobjecttyp:140,worri:[42,60,63,136],wors:[10,69],worst:[64,69],worth:67,would:[6,14,15,17,24,28,31,35,41,44,46,48,61,65,67,69,73,104,169,190,213,214],wouldn:69,wr0:[27,32,69,119],wr0_handler:72,wr1:[27,69,119],wr1_handler:72,wrap:[2,22,38,66,69,84,88,96,119],wrapper:[69,78,96,120,136,138],wrappers_pb2:[62,63],wrench:[6,119],wrench_trajectory_in_task:69,wrenchtrajectori:6,wrestl:69,wrist0:27,wrist1:27,wrist:[6,7,67,69],wrist_0:2,wrist_0_offset:[2,27],wrist_1:2,wrist_1_offset:[2,27],wrist_tform_hand:69,wrist_tform_hand_camera:69,wrist_tform_tool:[6,69],write:[6,17,18,22,25,36,37,38,40,46,60,62,63,69,73,83,86,88,89,93,96,101,113,137,144,146,148,149,156,159,191,196,199,236],write_animation_to_dest:73,write_data:148,write_data_block:144,write_descriptor_block:144,write_file_end:144,write_graph_and_snapshot:100,write_head:144,write_image_data:101,write_index:149,write_pgm_or_ppm:101,write_sync:[67,90],writefailederror:[118,137],writer:[69,142,149],written:[22,25,27,28,31,58,66,67,69,99,148,149,156,162],wrong:[6,20,44,61,62,63,64,65,66,69,78,92,93,96,98,104,113,115,118,146],wrongepocherror:104,wrote:[60,62],wrt:69,wsl:[65,66],www:65,x86_64:205,x_axi:69,x_bound:69,xarg:230,xbb:61,xbox360:245,xbox:[67,170,203],xbox_control:[67,245],xboxdrv:245,xhat:64,xinput:245,xmax:60,xmin:60,xml:[60,61],xml_dir:61,xoffset:69,xorg:56,xrdb:56,xresourc:56,xrx:69,xry:69,xrz:69,xstartup:56,xxx:[41,162,242],xy_admitt:69,xy_to_z_cross_term_admitt:69,xyz32f:215,xyz_32f:69,xyz_4sc:69,xyz_5c:69,xyz_5sc:69,xz8c9pl5tdztqyw:214,y_axi:69,y_bound:69,yaw:[2,16,27,31,32,33,41,48,64,66,69,110,117,119,161,245],yaw_bound:69,yaw_is_absolut:[2,32],yaw_rat:[2,32],yaw_vari:69,year:190,yellow:[17,240],yes:[56,69,139],yet:[66,67,69,104,113,118,122,136,143,160],yhat:64,yield:[29,64],yike:64,ymax:60,ymin:60,yoffset:69,you:[6,7,9,10,11,13,14,15,16,18,20,22,25,28,29,31,32,33,35,39,40,41,43,46,52,55,56,59,60,61,62,63,64,66,67,69,88,96,100,110,113,123,131,162,164,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,189,190,191,192,194,205,206,208,213,214,217,219,220,221,223,224,225,226,227,228,230,233,234,238,240,242,244,245],your:[2,9,12,17,19,20,23,29,33,40,43,55,59,60,61,62,63,64,66,67,69,88,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,188,190,194,205,213,215,216,217,219,220,221,222,224,225,226,227,228,231,233,235,237,241,242,244,245],your_bucket:224,your_ip:225,your_map:[17,215,216],your_robots_password:[60,62,63,64],yourcomput:65,youtub:31,yrx:69,yry:69,yrz:69,yyyi:190,yyyymmdd:[187,192],yyyymmdd_hhmmss:187,z_admitt:69,z_axi:69,z_bound:69,zbewqfkjbtznjrxu:66,zero:[2,22,25,32,37,61,64,69],zhat:64,zigzag:214,zip:[36,37,61,65,67],zipfil:65,zone:44,zoo:61,zoom:[22,29,69,132,216,240],zrx:69,zry:69,zrz:69,zstd:69,zulu:190,zxvf:61},titles:["Spot SDK","Choreography Protos","spot/choreography_params.proto","Concepts","About Spot","Spot Arm and Gripper","Arm Concepts","Arm Services","Arm and gripper specifications","Autonomy","AutoReturn Service","Autonomous navigation code examples","Navigation Services","Components of Navigation","Directed Exploration","Docking","GraphNav and Robot Locomotion","GraphNav Map Structure","GraphNav Service","Autonomy Technical Summary","GraphNav Initialization","GraphNav Localization","Mission Service","Typical Autonomous Use Case","Base Services","BDDF data format","Spot Choreography SDK","Animation Files for Choreographer","Animations in Choreography","Boston Dynamics Choreographer User Guide","Install Choreographer","Choreography Service","Choreography Moves Reference","Connecting Robots to Choreographer","Spot Data","Data Acquisition Output","Data Acquisition Overview","Data Buffer Overview","Developing API Services","E-Stop Service","Faults","Geometry and Frames","Lease Service","Machine Learning Bridge and External Compute","Networking","Robot Services","Integrate a Payload with the Data Acquisition Pipeline","Payload Developer Guide","Payload Software Interface","Running Custom Applications with Spot","Guidelines for Robust Payload Design","Mechanical Interfaces","Configuration Requirements","Electrical Interface","Mounting Rails","Spot CORE Cockpit - System Management Tool","Spot CORE VNC","API Protocol","Boston Dynamics API Protobuf Guidelines","Python Library","Tutorial: Playing Fetch with Spot","Fetch Part 2: Training the Model","Fetch Part 3: Evaluating the Model","Fetch Part 4: Autonomous Pick Up","Fetch Part 5: Detecting People and Playing Fetch","QuickStart","Understanding Spot Programming","Spot Release Notes","Basic Proto Definitions","arm_command.proto","Boston Dynamics Python Reference Guide","Python Choreography Client","Animation File Conversion Helpers","Animation File To Proto","Choreography","Python Client","Arm Surface Contact","Async Tasks","Auth","Auto Return","Bddf","Bddf Download","Channel","Command Line","Common","Data Acquisition","Data Acquisition Helpers","Data Acquisition Plugin","Data Acquisition Plugin Service","Data Acquisition Store","Data Buffer","Data Service","Directory","Directory Registration","Docking","Door","Estop","Exceptions","Fault","Frame Helpers","Graph Nav","Image","Image Service Helpers","Ir Enable Disable","Lease","License","Local Grid","Log Annotation","Manipulation Api Client","Map Processing","Math Helpers","Network Compute Bridge Client","Payload","Payload Registration","Point Cloud","Power","Processors","Recording","Robot","Robot Command","Robot Id","Robot State","Sdk","Server Util","SpotCAM Python Client","Audio","Compositor","Health","Lighting","Media Log","Network","Power","Ptz","Streamquality","Version","Spot Check","Time Sync","Token Cache","Token Manager","Util","World Object","Python Core","BDDF Python","Base Data Reader","Block Writer","Bosdyn","Common","Data Reader","Data Writer","File Indexer","Grpc Proto Reader","Grpc Reader","Grpc Service Reader","Grpc Service Writer","Message Reader","Pod Series Reader","Pod Series Writer","Protobuf Channel Reader","Protobuf Reader","Protobuf Series Writer","Stream Data Reader","Geometry","Util","Python Mission","Client","Constants","Exceptions","Remote Client","Server Util","Util","Python Examples","Animation Recorder","Arm and Mobility","Constrained Manipulation","Arm Door Command","Force Command","Arm Gaze Command","GCODE Drawing","Grasping","Arm JointMove Command","Arm Simple","Arm Deploy and Stow","Arm Surface Contact","Arm Trajectory","Walking to an Object","Arm and Mobility Follow","Auto Return Example","BDDF data download","Cloud Upload Example","Comms Testing","Data Acquisition Plugin Services","Using the Data Buffer service","Using the Robot Data Service","Manipulating Spot Service Configurations in Directory","Disable IR Emission","Robot Docking","Arm Examples","Autonomy and Missions Examples","Basic Service Examples","Data Acquisition Examples","Logging and Data Retrieval Examples","Payload Examples","Perception & World Objects Examples","Robot Behavior and Commands Examples","Creating an E-Stop endpoint","Follow a Fiducial","Frame Trajectory Commands","API Example - Visualize Depth in Visual Image","Using the Image Service","Retrieving Mission state","Using the Robot State Service","Performing Asynchronous State Queries on Spot","Using the World Object Service","Graph Nav Anchoring Optimization Example","GraphNav and Recording Service Command Line Interfaces","GraphNav Point Cloud Extractor","GraphNav Map Viewer","Hello Spot","Logging Through the API","Answering a Mission Question","Mission Recorder","Network Compute Bridge","Fire Extinguisher Detector Server","Using the Payload Service","Post Docking Callback Examples","Run and Interact with a RemoteMissionService.","Replaying a Mission","Interacting with a Ricoh Theta Camera","Payload & Service Initialization","Handling Service Faults","Spot CAM Services","Spot Detect and Follow","Responding to User Interaction via Light","Spot Tensorflow Object Detection","Adjusting Robot Stance In Place","Stitch Front Spot Images Together","Tester Programs","Using the Timesync Service","Upload and Execute Choreography Sequence","Using the Velodyne Point Cloud Service","Basic Streaming Visualizer for API Messages","Controlling the Robot with a Keyboard","Image Service for a Web Cam","World Object Mutations","Using World Object Service with Image Coordinates","Controlling the Robot with an Xbox Controller"],titleterms:{"break":67,"case":[23,50],"default":69,"export":222,"function":[46,66,67],"goto":32,"import":17,"new":[46,67],"return":[67,79,186],AWS:188,Adding:[29,41],NOT:10,Not:27,PPS:53,TLS:44,Use:23,Useful:56,Using:[10,19,44,46,59,191,192,208,210,212,223,225,237,239,244],about:[4,19,20],access:228,account:65,acquiredatarequest:69,acquiredatarespons:69,acquireleaserequest:69,acquireleaserespons:69,acquireplugindatarequest:69,acquireplugindatarespons:69,acquisit:[35,36,37,46,67,85,86,87,88,89,190,199,236],acquisitioncapabilitylist:69,acquisitionrequestlist:69,across:67,action:[22,36,69],actionidqueri:69,actual:215,add:191,addit:[42,55,67,220],addlogannotationrequest:69,addlogannotationrespons:69,adjust:234,adjustparamet:69,admittanceset:69,advanc:[29,42,67,170],aggregatedentri:69,all:27,allowableorient:69,anchor:[17,69,213],anchoredworldobject:69,anchorhintuncertainti:69,anchoringhint:69,angularinterpol:69,anim:[2,27,28,31,72,73,171],animatearm:2,animatebodi:2,animategripp:2,animateleg:2,animateparam:2,animatesingleleg:2,animationkeyfram:2,annot:[25,69,107],annotationsentri:69,annotationst:69,announc:228,answer:219,answeredquest:69,answerquestionrequest:69,answerquestionrespons:69,api:[3,31,38,48,57,58,67,108,207,218,240],apigraspedcarrystateoverrid:69,apigraspoverrid:69,apigraspoverriderequest:69,apigraspoverriderespons:69,applic:49,apriltag:205,apriltagposestatu:69,apriltagproperti:69,architectur:36,area:69,arm:[5,6,7,8,27,32,52,67,76,172,174,176,179,180,181,182,183,185,196],arm_command:69,arm_mov:32,arm_move_rel:32,arm_surface_contact:69,arm_surface_contact_servic:69,armcartesiancommand:69,armcommand:[6,69],armdragcommand:69,armjointangl:2,armjointmovecommand:69,armjointposit:69,armjointtrajectori:69,armjointtrajectorypoint:69,armjointveloc:69,armmovefram:2,armmoveparam:2,armparam:69,armplayback:2,armstopcommand:69,armsurfacecontact:[7,69],armsurfacecontactcommand:69,armsurfacecontactrespons:69,armsurfacecontactservic:69,armvelocitycommand:69,associ:69,associatedmetadata:69,async:77,asynchron:[66,211],attach:[46,223],audio:[69,125],audiocapturechannel:69,audioservic:69,auth:[24,69,78],auth_servic:69,authent:66,author:[37,48,66,67],authservic:69,auto:[67,79,186],auto_return:69,auto_return_servic:69,autograspcommand:69,automat:17,autonom:[11,16,23,63,67],autonomi:[9,19,197],autopushcommand:69,autoreturn:10,autoreturnservic:69,autowalk:[188,225],avoid:6,awbmod:69,awbmodeenum:69,axismod:69,background:[46,213],base:[24,143],basic:[29,38,42,68,187,198,240],basic_command:69,batterychangeposecommand:69,batteryst:69,bddf:[25,37,69,80,81,142,187],beginn:29,behavior:[22,40,67,203],behaviorfault:69,behaviorfaultst:69,beta:67,between:[41,228],big:8,blackboard:22,blob:[37,191],blobpag:69,blobspec:69,block:[29,66,144],blockentri:69,bodi:[6,27,32],body_const:32,body_hold:32,bodycontrolparam:69,bodyexternalforceparam:69,bodyholdparam:2,bodymovementstatu:69,boot:56,bosdyn:[67,145],bosdyndockst:69,bosdyngraphnavloc:69,bosdyngraphnavst:69,bosdynnavigaterout:69,bosdynnavigateto:69,bosdynpowerrequest:69,bosdynrecordev:69,bosdynrobotcommand:69,bosdynrobotst:69,boston:[29,37,58,70],both:27,bound:[48,69],boundingboxproperti:69,bourre:32,bourreeparam:2,box2:69,box2withfram:69,box3:69,box3withfram:69,box:48,bridg:[43,67,111,190,221],brightnessesentri:69,buffer:[37,44,67,90,191],bug:67,build:[49,219,222],butt_circl:32,buttcircleparam:2,cabl:[50,51],cach:137,calibr:69,callback:[224,225],cam:[67,230,242],camera:[4,46,66,69,216,227,240],cameracalibrationcommandrequest:69,cameracalibrationcommandrespons:69,cameracalibrationfeedbackrequest:69,cameracalibrationfeedbackrespons:69,camerainterfac:46,cameraintrins:69,cameraresultsentri:69,can:[8,17,65,67],cancelacquisitionrequest:69,cancelacquisitionrespons:69,capabl:[36,46],captur:[36,46,66],captureactionid:69,captureparamet:69,carryst:69,cartesian:6,cartesianveloc:69,categori:[2,53],caus:69,cellformat:69,center:48,chalk:177,chang:[67,234],channel:[36,82,157],check:[55,135],checkin:39,checksumtyp:69,chicken_head:32,chickenheadparam:2,childtoparentedgemapentri:69,choic:44,choreograph:[27,29,30,33,67],choreographerdisplayinfo:2,choreographersav:2,choreographi:[1,26,28,29,31,32,67,71,74,238],choreography_param:2,choreography_sequ:2,choreography_servic:2,choreographysequ:2,choreographyservic:2,choreographystatelog:2,circl:69,circle2d:69,clapparam:2,clawgrippercommand:69,clear:66,clearanc:52,clearbehaviorfaultrequest:69,clearbehaviorfaultrespons:69,clearbiteventsrequest:69,clearbiteventsrespons:69,cleargraphrequest:69,cleargraphrespons:69,clearservicefaultrequest:69,clearservicefaultrespons:69,client:[31,43,67,71,75,108,111,124,163,164,167,221,228],closur:17,cloud:[67,114,188,190,215,239],cockpit:55,code:[11,67,69,70,213],collect:46,collis:6,collisioncheckingparam:69,color:[2,69],colormap:69,column:27,comm:189,command:[45,49,65,66,67,69,83,119,174,175,176,179,203,204,206,214,220],comment:[27,37,187,191,227],common:[84,146,171,172,174,175,176,177,178,179,180,181,182,183,184,185,217],commonerror:69,commsstat:69,commun:[53,67,190,228],compar:69,completionbehavior:69,complex:22,compon:[13,43],compositor:[69,126],compositorservic:69,compress:69,comput:[43,49,67,111,190,221],concept:[3,6,19,36,43],condit:[31,69],config:[15,31],configrang:69,configur:[10,39,48,49,52,66,193,224,227],configurerequest:69,configurerespons:69,connect:[4,33,213,227],connector:51,consist:[67,213],constant:165,constantresult:69,constantvalu:69,constrain:[67,173],constrainedmanipulationcommand:69,constraint:21,contact:[69,76,182],contain:[49,55],content:[0,1,3,5,9,26,34,47,57,59,60,68,70,71,75,124,141,142,163,170,196,197,198,199,200,201,202,203],control:[17,27,29,33,67,216,240,241,245],convers:72,coordin:244,copi:[29,65],core:[55,56,141,228],correct:51,crash:50,crawl:32,crawlparam:2,creat:[17,22,46,49,56,66,204],createedgerequest:69,createedgerespons:69,createwaypointrequest:69,createwaypointrespons:69,credenti:188,csv:35,custom:49,cyclepowerrequest:69,cyclepowerrespons:69,cylindricalcoordin:69,cylindricalveloc:69,data:[17,19,25,34,35,36,37,43,46,67,85,86,87,88,89,90,91,143,147,148,160,187,190,191,192,199,200,213,236],data_acquisit:69,data_acquisition_plugin_servic:69,data_acquisition_servic:69,data_acquisition_stor:69,data_acquisition_store_servic:69,data_buff:69,data_buffer_servic:69,data_chunk:69,data_index:69,data_servic:69,dataacquisit:69,dataacquisitioncap:69,dataacquisitionpluginservic:69,dataacquisitionservic:69,dataacquisitionstoreservic:69,datablob:69,databufferservic:69,databufferstatu:69,datacaptur:69,datachunk:69,datadescriptor:69,dataerror:69,dataidentifi:69,dataindex:69,dataqueri:69,dataqueryparam:69,dataservic:69,datetoblackboard:69,db25:51,debug:[227,242],debugrequest:69,debugrespons:69,defin:[66,213],defineblackboard:69,definit:[43,68],degrad:69,degradationtyp:69,delet:29,delete_pag:192,deletedatapagesrequest:69,deletedatapagesrespons:69,deletepagestatu:69,deleterequest:69,deleterespons:69,deletesoundrequest:69,deletesoundrespons:69,demo:228,depend:[67,70,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,189,190,191,192,193,194,195,204,205,207,208,209,210,211,212,213,214,215,216,217,218,219,220,223,225,226,229,230,232,234,236,237,238,239,240,241,243,244,245],deploi:181,deprec:67,depth:207,depthplanespotcheckresult:69,deregisterestopendpointrequest:69,deregisterestopendpointrespons:69,descriptorblock:69,desert:21,design:50,detach:223,detail:177,detect:[64,231,233],detector:222,develop:[38,47,67,227],devic:48,diagram:221,dimens:[4,51,54],direct:14,directionconstraint:69,directionhint:69,directori:[24,38,46,69,92,93,193],directory_registr:69,directory_registration_servic:69,directory_servic:69,directoryregistrationservic:[48,69],directoryservic:[48,69],disabl:[14,67,103,194],discov:43,discoveri:44,distanc:205,distribut:65,dock:[15,67,69,94,195,224],dockedstatu:69,docker:[49,55,189,190,221,224,227,231,242],docking_servic:69,dockingcommandfeedbackrequest:69,dockingcommandfeedbackrespons:69,dockingcommandrequest:69,dockingcommandrespons:69,dockingservic:69,dockproperti:69,dockstat:69,docktyp:69,document:[55,67],door:[7,67,69,95,174],door_servic:69,doorcommand:69,doorservic:69,download:[17,36,37,81,187,190],downloadedgesnapshotrequest:69,downloadedgesnapshotrespons:69,downloadgraphrequest:69,downloadgraphrespons:69,downloadrobotstatelogrequest:2,downloadrobotstatelogrespons:2,downloadwaypointsnapshotrequest:69,downloadwaypointsnapshotrespons:69,drag:6,draw:177,drawablearrow:69,drawablebox:69,drawablecapsul:69,drawablecylind:69,drawablefram:69,drawablelinestrip:69,drawablepoint:69,drawableproperti:69,drawablespher:69,dure:41,dynam:[29,32,37,58,70],eas:2,edg:[17,19,21,50,69],edgesnapshot:69,edgesourc:69,either:27,electr:53,emiss:194,emitt:67,empti:69,enabl:[14,50,56,67,103],enablecongestioncontrolrequest:69,enablecongestioncontrolrespons:69,encod:69,encodingparamet:69,endpoint:[66,69,204,205,232],ensur:17,entri:31,environ:[4,19,65],environment:67,error:[16,42,44,46,69],errorcod:69,establish:66,establishsessionrequest:69,establishsessionrespons:69,estim:67,estop:[45,69,96],estop_servic:69,estopcheckinrequest:69,estopcheckinrespons:69,estopconfig:69,estopendpoint:69,estopendpointwithstatu:69,estopservic:69,estopst:69,estopstoplevel:69,estopsystemstatu:69,eulerratezyxvalu:2,eulerzyxvalu:2,evalu:62,event:[37,69,187,191],eventscom:69,eventscommentsspec:69,eventspec:69,exampl:[11,17,22,31,37,38,43,46,48,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,223,224,225,226,229,230,232,234,237,238,239,240,241,242,243,244],except:[67,97,166],execut:[214,221,222,231,233,235,238,242,245],executechoreographyrequest:2,executechoreographyrespons:2,exit:31,expand:67,explor:14,expos:67,extens:27,extent:48,extern:[43,50,205],externalforceind:69,extinguish:222,extractor:215,fade_color:32,fadecolorparam:2,failednod:69,fault:[40,48,67,98,229],fault_servic:69,faultservic:69,featur:[21,67],featureenabledentri:69,featurequalitytoler:69,feedback:[6,16,67,69],fetch:[60,61,62,63,64],fidget_stand:32,fidgetpreset:2,fidgetstandparam:2,fiduci:[20,205,220],fiducialinit:69,fiducialloopclosureparam:69,figure8_mov:32,figure8param:2,file:[25,27,29,31,35,72,73,149],fileformatdescriptor:69,fileformatvers:69,fileindex:69,finish:10,fire:222,fix:67,follow:[185,205,231],followarmcommand:[6,69],footheightcheckresult:69,footheightresultsentri:69,footpositionsentri:69,footstat:69,forc:[6,175],fordur:69,format:[25,37,69],formatblackboard:69,forward:48,frame:[6,41,66,67,99,206],frame_snapshot:32,framesnapshotparam:2,frametreesnapshot:69,freezecommand:69,from:[17,65,190],front:[51,235],front_up:32,frontupparam:2,full:65,full_body_command:69,fullbodycommand:69,fundament:66,gaze:[6,176],gazecommand:69,gcode:177,gcp:188,geometri:[20,41,51,69,161],get:[65,208],get_com:192,get_ev:192,get_index:192,get_pag:192,getaudiocapturechannelrequest:69,getaudiocapturechannelrespons:69,getaudiocapturegainrequest:69,getaudiocapturegainrespons:69,getauthtokenrequest:69,getauthtokenrespons:69,getbitstatusrequest:69,getbitstatusrespons:69,getconfigurationrequest:69,getconfigurationrespons:69,getdatabufferstatusrequest:69,getdatabufferstatusrespons:69,getdataindexrequest:69,getdataindexrespons:69,getdatapagesrequest:69,getdatapagesrespons:69,getdockingconfigrequest:69,getdockingconfigrespons:69,getdockingstaterequest:69,getdockingstaterespons:69,getestopconfigrequest:69,getestopconfigrespons:69,getestopsystemstatusrequest:69,getestopsystemstatusrespons:69,geteventscommentsrequest:69,geteventscommentsrespons:69,getfeatureenabledrequest:69,getfeatureenabledrespons:69,geticeconfigurationrequest:69,geticeconfigurationrespons:69,getimagerequest:69,getimagerespons:69,getinforequest:69,getinforespons:69,getircolormaprequest:69,getircolormaprespons:69,getledbrightnessrequest:69,getledbrightnessrespons:69,getlicenseinforequest:69,getlicenseinforespons:69,getlocalgridsrequest:69,getlocalgridsrespons:69,getlocalgridtypesrequest:69,getlocalgridtypesrespons:69,getlocalizationstaterequest:69,getlocalizationstaterespons:69,getmissionrequest:69,getmissionrespons:69,getnetworksettingsrequest:69,getnetworksettingsrespons:69,getpayloadauthtokenrequest:69,getpayloadauthtokenrespons:69,getpointcloudrequest:69,getpointcloudrespons:69,getpowerstatusrequest:69,getpowerstatusrespons:69,getptzpositionrequest:69,getptzpositionrespons:69,getptzvelocityrequest:69,getptzvelocityrespons:69,getrecordstatusrequest:69,getrecordstatusrespons:69,getscreenrequest:69,getscreenrespons:69,getserviceentryrequest:69,getserviceentryrespons:69,getserviceinforequest:69,getserviceinforespons:69,getsoftwareversionrequest:69,getsoftwareversionrespons:69,getsslcertrequest:69,getsslcertrespons:69,getstaterequest:69,getstaterespons:69,getstatusrequest:69,getstatusrespons:69,getstreamparamsrequest:69,getstreamparamsrespons:69,gettemperaturerequest:69,gettemperaturerespons:69,getvisiblecamerasrequest:69,getvisiblecamerasrespons:69,getvolumerequest:69,getvolumerespons:69,github:65,gland:51,good:17,gotoparam:2,graph:[67,69,100,213],graph_nav:[69,213],graph_nav_servic:69,graphnav:[16,17,18,19,20,21,214,215,216],graphnavrecordingservic:[18,69],graphnavservic:[18,69],grasp:[8,173,178],graspparam:69,grasppositionconstraint:69,gratedsurfacesmod:69,grid:[45,106],gripper:[5,8,27,32,67],gripper_command:69,grippercommand:69,gripperparam:2,group:36,grpc:[38,44,150,151,152,153,187],grpcpage:69,grpcspec:69,gui:[187,204],guid:[29,47,70,231,233,245],guidelin:[40,50,58],hand:6,handl:[44,67,229],handlestal:69,handletyp:69,handpos:2,happen:10,hardwareconfigur:69,header:69,health:[69,127],healthservic:69,height:52,hello:[65,217],helper:[67,72,86,99,102,110],high:36,hingesid:69,hip:4,hiprangeofmotionresult:69,hiprangeofmotionresultsentri:69,hop:32,hopparam:2,how:[8,10,14,66,220],http:44,iceserv:69,icpparam:69,identifi:25,imag:[45,46,49,66,69,101,102,207,208,227,235,236,242,244],image_servic:69,imageacquisitioncap:69,imagecaptur:69,imageparam:69,imageproperti:69,imagerequest:69,imagerespons:69,imageservic:69,imagesourc:69,imagesourceandservic:69,imagesourcecaptur:69,imagetyp:69,impairedstatu:69,implement:36,improv:67,inclus:2,independ:[65,221],independent_color:32,independentcolorparam:2,index:149,inertia:48,infrastructur:38,ingress:50,initi:[19,20,228],initializelensrequest:69,initializelensrespons:69,inspect:66,instal:[30,56,65,188,221,224,227,231,233,235,245],instruct:245,integr:[46,67],interact:[225,227,232],interfac:[29,31,38,48,51,53,214],interfer:[50,52],intermedi:170,intern:37,interpret:213,interv:21,introduct:47,introductori:170,invok:14,ir_enable_dis:69,ir_enable_disable_servic:69,ircolormap:69,irenabledisablerequest:69,irenabledisablerespons:69,irenabledisableservic:69,irmeteroverlai:69,isometr:52,issu:[67,235],item:[227,235],jog:32,joint:[4,6,8],jointkinematiccheckresult:69,jointlimit:69,jointmov:179,jointstat:69,joystick:33,json:35,jump:32,jumpparam:2,keepout:51,kei:19,keyboard:[29,33,220,241],keyfram:27,keypoint:69,keypointmatch:69,keypointset:69,keypointtyp:69,keytoseriesidentifierhashentri:69,keyvalu:69,keyword:27,kinematiccalresultsentri:69,kinematicst:69,kneel:32,kneel_circl:32,kneel_leg_mov:32,kneel_leg_move2:32,kneel_to_stand:32,kneel_to_stand_fast:32,kneelcircleparam:2,kneellegmove2param:2,kneellegmoveparam:2,known:[67,235],land:69,layer:31,lead:2,learn:43,leas:[24,42,66,67,69,104],lease_servic:69,leaseown:69,leaseresourc:69,leaseservic:69,leaseuseresult:69,led:69,ledlight:2,leg:[2,27,50,52],legjointangl:2,legpaircheckresult:69,legpairresultsentri:69,length:[8,52],let:65,level:[36,69],librari:[59,205],licens:[67,69,105],license_servic:69,licenseinfo:69,licenseservic:69,light:[32,128,232],lightingservic:69,lightsid:2,limit:[6,69],line:[49,67,83,204,214],link:[8,67,69],linkstatu:69,list:[46,66],listallmovesrequest:2,listallmovesrespons:2,listavailablemodelsrequest:69,listavailablemodelsrespons:69,listavailablemodelsstatu:69,listcamerasrequest:69,listcamerasrespons:69,listcaptureactionsrequest:69,listcaptureactionsrespons:69,listimagesourcesrequest:69,listimagesourcesrespons:69,listleasesrequest:69,listleasesrespons:69,listlogpointsrequest:69,listlogpointsrespons:69,listpayloadsrequest:69,listpayloadsrespons:69,listpointcloudsourcesrequest:69,listpointcloudsourcesrespons:69,listptzrequest:69,listptzrespons:69,listscreensrequest:69,listscreensrespons:69,listserviceentriesrequest:69,listserviceentriesrespons:69,listsoundsrequest:69,listsoundsrespons:69,liststoreddatarequest:69,liststoreddatarespons:69,liststoredimagesrequest:69,liststoredimagesrespons:69,liststoredmetadatarequest:69,liststoredmetadatarespons:69,listworldobjectrequest:69,listworldobjectrespons:69,load:[29,50,213],loadcellresultsentri:69,loadcellspotcheckresult:69,loadmissionrequest:69,loadmissionrespons:69,loadsoundrequest:69,loadsoundrespons:69,local:[19,20,21,45,49,69,106,220],local_grid:69,local_grid_servic:69,localgrid:69,localgridext:69,localgridrequest:69,localgridrespons:69,localgridservic:69,localgridtyp:69,localizeregion:69,locomot:[16,67],locomotionhint:69,log:[28,31,37,67,69,107,129,191,200,218],log_annot:69,log_annotation_servic:69,logannot:69,logannotationlogblob:69,logannotationoperatormessag:69,logannotationservic:69,logannotationtextmessag:69,loggedfootcontact:2,loggedjoint:2,loggedstatekeyfram:2,logpoint:69,logpreservehint:69,logstatu:69,logtyp:2,loop:17,lost:[19,21],lostdetectorst:69,machin:43,manag:[49,55,65,138],manipul:[7,67,108,173,193],manipulation_api:69,manipulation_api_servic:69,manipulationapifeedbackrequest:69,manipulationapifeedbackrespons:69,manipulationapirequest:69,manipulationapirespons:69,manipulationapiservic:69,manipulationcamerasourc:69,manipulationfeedbackst:69,manipulatorst:69,map:[17,19,67,69,109,216],map_process:69,map_processing_servic:69,mapprocessingservic:69,mapstat:69,mass:48,match:69,math:[41,110],matrix:69,measurementparam:69,mechan:51,media:129,medialogservic:69,messag:[37,66,154,240],messagetypedescriptor:69,metadata:[35,46,69],metric:213,miscellan:67,mission:[22,67,69,163,197,209,219,220,225,226],mission_question_answer:219,mission_servic:69,missioninfo:69,missionservic:[22,69],mobil:[6,67,172,185],mobility_command:69,mobilitycommand:69,mobilityparam:69,mode:[29,69],model:[43,61,62],modellabel:69,modifi:[17,29],moment:48,momentofintertia:69,more:22,motion:8,motiv:25,motor:[53,66],motorpowerst:69,mount:54,mountframenam:69,move:[6,29,32,65,66,67],moveinfo:2,moveinfoconfig:31,movement:205,moveparam:2,moveparamsconfig:31,multipl:[29,65],music:29,mutat:[69,243],mutateworldobjectrequest:69,mutateworldobjectrespons:69,name:27,namedarmpositionscommand:69,nav:[67,69,100,213],navig:[11,12,13,16,18,67],navigaterout:16,navigaterouterequest:69,navigaterouterespons:69,navigateto:16,navigatetoanchorrequest:69,navigatetoanchorrespons:69,navigatetorequest:69,navigatetorespons:69,navigationfeedbackrequest:69,navigationfeedbackrespons:69,network:[44,48,55,67,69,111,130,190,221],network_compute_bridg:69,network_compute_bridge_servic:69,network_stat:69,networkcomputebridg:[43,69],networkcomputebridgework:[43,69],networkcomputeinputdata:69,networkcomputerequest:69,networkcomputerespons:69,networkcomputeserverconfigur:69,networkcomputestatu:69,networkservic:69,networktupl:69,next:65,nod:32,node:[22,69,219],nodeinfo:69,nodest:69,nodestatesattick:69,non:46,normalizedcoordin:69,note:[6,19,31,67],object:[8,45,66,140,173,184,202,212,233,240,243,244],objmodel:69,obstacleparam:69,odometryloopclosureparam:69,off:66,offset:234,one:225,onli:231,open:67,opendoorcommandrequest:69,opendoorcommandrespons:69,opendoorfeedbackrequest:69,opendoorfeedbackrespons:69,oper:[37,39,41,187,191],operand:69,operatorcom:69,optim:[17,213],optimizerparam:69,option:[27,67,69],organ:25,orient:[48,51],origin:21,other:[21,43,46,67,213],output:[35,207],over:67,overrid:69,overview:[29,31,36,37,56,220],ownership:66,pace2stepparam:2,pace:32,pace_2step:32,packag:[65,188,224,227,235],pageformat:69,pageinfo:69,pagesandtimestamp:69,param:69,paramet:[27,29,69],parentedg:69,part:[61,62,63,64,225],particular:27,password:56,past:29,pattern:[2,42],pausemissionrequest:69,pausemissionrespons:69,payload:[4,46,47,48,49,50,52,53,67,69,112,113,201,223,228],payload_estim:69,payload_registr:69,payload_registration_servic:69,payload_servic:69,payloadcheckresult:69,payloadcredenti:69,payloadestimationcommand:69,payloadmassvolumeproperti:69,payloadportspowerst:69,payloadpreset:69,payloadregistrationservic:[48,69],payloadservic:[48,69],peopl:64,per:53,percept:202,perform:[29,67,211],pertain:27,pick:63,pickobject:69,pickobjectexecuteplan:69,pickobjectinimag:69,pickobjectrayinworld:69,pin:53,pinch:8,ping:65,pinholeintrins:69,pinholemodel:69,pinout:53,pip:65,pipelin:46,pivot:2,pixelformat:69,place:[20,234],plai:[60,64],plane:69,plannerstatu:69,platform:59,playmissionrequest:69,playmissionrespons:69,playset:69,playsoundrequest:69,playsoundrespons:69,plugin:[46,67,87,88,190,236],pluginserviceerror:69,pod:[155,156],podtypedescriptor:69,podtypeenum:69,point:[8,54,67,114,190,215,239],point_cloud:69,point_cloud_servic:69,pointcloud:69,pointcloudrequest:69,pointcloudrespons:69,pointcloudservic:69,pointcloudsourc:69,polygon:69,polygonwithexclus:69,polylin:69,poor:67,port:[48,52,53],portain:49,pose:6,posebound:69,posit:[48,69],positionalinterpol:69,post:[43,224],power:[4,45,53,66,67,69,115,131],power_servic:69,powercommandfeedbackrequest:69,powercommandfeedbackrespons:69,powercommandrequest:69,powercommandrespons:69,powercommandstatu:69,powerservic:69,powerst:69,powerstatu:69,pre:43,predefin:6,prepar:[56,67],prepposebehavior:69,prerequisit:[55,242],preview:29,problem:[171,172,174,175,176,177,178,179,180,181,182,183,184,185,213,217],process:[17,43,109],processanchoringrequest:69,processanchoringrespons:69,processor:116,processtopologyrequest:69,processtopologyrespons:69,program:[66,171,172,174,175,176,177,178,179,180,181,182,183,184,185,194,217,236],prompt:[69,219],properti:48,protect:50,proto:[1,2,68,69,73,150],protobuf:[58,66,67,157,158,159,191],protocol:[44,57],ptz:[69,132],ptzdescript:69,ptzposit:69,ptzservic:69,ptzveloc:69,puls:53,pushbar:67,python:[17,31,59,65,66,70,71,75,124,141,142,163,170],quad:69,quaternion:69,queri:[65,211],question:[69,219],queuestatu:69,quickstart:65,radian:48,rai:69,rail:54,random_rot:32,random_stretch:32,randomrotateparam:2,rang:[8,192],raw:37,rayproperti:69,reader:[143,147,150,151,152,154,155,157,158,160],recommend:170,record:[17,67,69,117,171,214,220,225],recorddatablobsrequest:69,recorddatablobsrespons:69,recordeventsrequest:69,recordeventsrespons:69,recording_servic:69,recordingenviron:69,recordoperatorcommentsrequest:69,recordoperatorcommentsrespons:69,recordsignalticksrequest:69,recordsignalticksrespons:69,recordtextmessagesrequest:69,recordtextmessagesrespons:69,recordtyp:69,red:29,refer:[32,54,70],regist:[38,48,66,228],registerestopendpointrequest:69,registerestopendpointrespons:69,registerpayloadrequest:69,registerpayloadrespons:69,registerservicerequest:69,registerservicerespons:69,registersignalschemarequest:69,registersignalschemarespons:69,registr:[39,46,48,93,113,223,228],rel:51,releas:67,reliabl:31,remot:[69,167,225],remote_servic:69,remotegrpc:69,remotemissionservic:[22,69,225],remotepointcloudstatu:69,remov:67,renam:67,repeat:69,replai:[219,220,226],report:46,represent:42,request:[6,36,65,69,187],requesthead:69,requir:[30,52,53,65,224,227,235],resourc:[42,56],resourcetre:69,respond:232,respons:187,responsehead:69,restartmissionrequest:69,restartmissionrespons:69,restartwhenpaus:69,result:[69,213],resumebehavior:69,retainleas:69,retainleaserequest:69,retainleaserespons:69,retri:69,retriev:[66,200,209],retrieverawdatarequest:69,retrieverawdatarespons:69,retrieverequest:69,retrieverespons:69,returnleaserequest:69,returnleaserespons:69,ricoh:227,right:50,ripple_color:32,ripplecolorparam:2,robot:[4,15,16,19,21,24,29,33,38,41,44,45,51,52,53,65,66,67,118,119,120,121,173,192,195,203,205,210,213,221,225,234,241,245],robot_command:69,robot_command_servic:69,robot_id:69,robot_id_servic:69,robot_st:69,robot_state_servic:69,robotcommand:69,robotcommandfeedback:69,robotcommandfeedbackrequest:69,robotcommandfeedbackrespons:69,robotcommandfeedbackstatu:69,robotcommandrequest:69,robotcommandrespons:69,robotcommandservic:69,robothardwareconfigurationrequest:69,robothardwareconfigurationrespons:69,robotid:69,robotidrequest:69,robotidrespons:69,robotidservic:69,robotimpairedst:69,robotlinkmodelrequest:69,robotlinkmodelrespons:69,robotmetr:69,robotmetricsrequest:69,robotmetricsrespons:69,robotpowerst:69,robotsoftwarereleas:69,robotst:69,robotstaterequest:69,robotstaterespons:69,robotstateservic:69,robust:50,rom:52,rotate_bodi:32,rotate_body_sharp:32,rotatebodyparam:2,rotateimag:69,rotationset:69,rotationwithtoler:69,rout:69,routeblockedbehavior:69,routefollowingparam:69,routegenparam:69,rpc:[18,22,43,48,75,163],run:[29,30,46,49,65,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,204,205,206,207,208,209,210,211,212,213,215,216,217,218,223,224,225,226,227,229,230,231,232,234,237,238,239,240,241,242,243,244],running_man:32,runningmanparam:2,safe:10,safepoweroffcommand:69,safeti:[6,8,29,53],sampl:67,save:29,scalartrajectori:69,scalartrajectorypoint:69,scalingpair:69,screendescript:69,script:228,sdk:[0,26,65,66,122],se2pos:69,se2trajectori:69,se2trajectorycommand:[67,69],se2trajectorypoint:69,se2veloc:69,se2velocitycommand:69,se2velocitylimit:69,se3covari:69,se3pos:69,se3trajectori:69,se3trajectorypoint:69,se3veloc:69,seal:51,search:20,second:53,section:27,see:65,select:[29,228],selector:69,self:[48,50],self_right:32,selfrightcommand:69,sens:4,sequenc:[22,29,69,238],seri:[25,155,156,159],seriesblockindex:69,seriesdescriptor:69,seriesidentifi:69,server:[38,43,56,123,168,221,222,225],servertyp:69,servic:[7,10,12,15,18,22,24,31,36,37,38,39,40,42,45,46,48,55,66,67,69,88,91,102,152,153,190,191,192,193,198,208,210,212,214,223,227,228,229,230,236,237,239,242,244],service_fault:69,serviceentri:69,servicefault:69,servicefaultid:69,servicefaultst:69,set:[55,213,228],set_color:32,setaudiocapturechannelrequest:69,setaudiocapturechannelrespons:69,setaudiocapturegainrequest:69,setaudiocapturegainrespons:69,setblackboard:69,setcolorparam:2,setestopconfigrequest:69,setestopconfigrespons:69,seticeconfigurationrequest:69,seticeconfigurationrespons:69,setircolormaprequest:69,setircolormaprespons:69,setirmeteroverlayrequest:69,setirmeteroverlayrespons:69,setledbrightnessrequest:69,setledbrightnessrespons:69,setlocalizationrequest:69,setlocalizationrespons:69,setpassphraserequest:69,setpassphraserespons:69,setpowerstatusrequest:69,setpowerstatusrespons:69,setptzpositionrequest:69,setptzpositionrespons:69,setptzvelocityrequest:69,setptzvelocityrespons:69,setrecordingenvironmentrequest:69,setrecordingenvironmentrespons:69,setscreenrequest:69,setscreenrespons:69,setstreamparamsrequest:69,setstreamparamsrespons:69,setup:[55,65,66,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,189,190,191,192,193,194,195,204,205,207,208,209,210,211,212,213,214,215,216,217,218,219,220,223,225,226,229,230,232,234,236,237,238,239,240,241,243,244],setvolumerequest:69,setvolumerespons:69,sever:69,shock:50,shorepowerst:69,shoulder_left:32,shoulder_right:32,side:2,sideparam:2,signal:37,signalschema:69,signalschemaid:69,signaltick:69,simpl:[22,180],simpleparallel:69,sit:32,sit_to_sprawl:32,sitcommand:69,skeleton:69,skip:32,sleep:69,slice:31,slider:29,softwar:[48,55,56,66],softwarevers:69,sound:69,span:187,sparse_featur:69,specentri:69,specif:[4,8,27,53,245],specifi:[67,187],sphericallimit:69,spot:[0,2,3,4,5,15,26,34,41,47,49,54,55,56,60,65,66,67,69,135,171,172,174,175,176,177,178,179,180,181,182,183,184,185,193,194,211,217,227,228,230,231,233,235],spot_cam:69,spot_check:69,spot_check_servic:69,spotcam:[67,124],spotcaml:69,spotcamptz:69,spotcamresetautofocu:69,spotcamstoremedia:69,spotcheckcommandrequest:69,spotcheckcommandrespons:69,spotcheckfeedbackrequest:69,spotcheckfeedbackrespons:69,spotcheckservic:69,squeezegrasp:69,stair:[21,69],stairdata:69,stairtransform:69,stanc:[69,234],stancecommand:69,stand_to_kneel:32,stand_up:32,standard:[2,69,70],standcommand:69,start:[38,56,219,225,228],startrecordingrequest:69,startrecordingrespons:69,startrecordingstaterequest:2,startrecordingstaterespons:2,startrequest:69,startrespons:69,startroutebehavior:69,state:[15,45,66,67,69,121,209,210,211],statu:[2,16,69],step:[32,65,213,225,227,235],stepparam:2,stitch:235,stop:[6,39,65,66,204,205,206,232,238],stopcommand:69,stopmissionrequest:69,stopmissionrespons:69,stoprecordingrequest:69,stoprecordingrespons:69,stoprecordingstaterequest:2,stoprecordingstaterespons:2,stoprequest:69,stoprespons:69,store:[89,190],storedatarequest:69,storedatarespons:69,storeimagerequest:69,storeimagerespons:69,storemetadata:69,storemetadatarequest:69,storemetadatarespons:69,storerequest:69,storerespons:69,stow:[32,181],stowstat:69,straightstaircas:69,stream:[69,160,240],streamparam:69,streamqual:[69,133],streamqualityservic:69,structtypedescriptor:69,structur:[17,22,25,27,35,38,216],stuck:21,summari:19,support:[27,37,59,67],surfac:[76,182],suspectedambigu:69,swai:32,swayparam:2,swaystyl:2,swingdirect:69,swingheight:69,sync:[24,136],synchronized_command:69,synchronizedcommand:[6,69],system:[30,36,40,43,55,65,221],systemfault:69,systemfaultst:69,tabl:48,tablet:17,tagrequest:69,tagrespons:69,take:[65,66],takeleaserequest:69,takeleaserespons:69,task:77,tasktyp:69,tcp:44,teardownsessionrequest:69,teardownsessionrespons:69,technic:19,temperatur:69,tensor:48,tensorflow:233,termin:55,terminolog:31,terrainparam:69,terrainst:69,test:[46,49,189,190,227,236],tester:236,text:37,textmessag:69,textur:20,theta:227,thread:46,through:218,tickrequest:69,tickrespons:69,tigervnc:56,time:[24,136,187,192],time_rang:69,time_sync:69,time_sync_servic:69,timerang:69,timerangequeri:69,timesync:[66,237],timesyncestim:69,timesyncroundtrip:69,timesyncservic:69,timesyncst:69,timesyncupdaterequest:69,timesyncupdaterespons:69,tip:[46,227,242],togeth:235,token:[137,138],tool:[55,67],top:52,topolog:17,total:48,track:[27,31],train:61,trajectori:[69,183,206],transfer:17,transform:41,transit:32,transitionst:2,travelparam:69,tree:22,triggerservicefaultrequest:69,triggerservicefaultrespons:69,trot:32,troubleshoot:221,turn_2step:32,turnparam:2,tutori:60,twerk:32,twerkparam:2,two:225,txt:31,type:[2,22,37,69],typic:[15,23],under:52,understand:[42,66,171,172,174,175,176,177,178,179,180,181,182,183,184,185,194,213,215,216,217],undock:15,unit:27,unregisterservicerequest:69,unregisterservicerespons:69,unstow:32,updat:[21,67],updatepayloadattachedrequest:69,updatepayloadattachedrespons:69,updatepayloadversionrequest:69,updatepayloadversionrespons:69,updateservicerequest:69,updateservicerespons:69,upload:[17,188,213,238],uploadanimatedmoverequest:2,uploadanimatedmoverespons:2,uploadchoreographyrequest:2,uploadchoreographyrespons:2,uploadedgesnapshotrequest:69,uploadedgesnapshotrespons:69,uploadgraphrequest:69,uploadgraphrespons:69,uploadwaypointsnapshotrequest:69,uploadwaypointsnapshotrespons:69,usag:[15,42,67],use:10,user:[29,65,231,232,233,245],userdata:69,using:[31,190,224,227,242],util:[69,123,139,162,168,169],valu:[69,228],variabl:69,variabledeclar:69,vec2:69,vec2valu:69,vec3:69,vec3trajectori:69,vec3trajectorypoint:69,vec3valu:69,vectoralignmentwithtoler:69,veloc:6,velodyn:239,verifi:65,version:[55,59,69,134,204],versionservic:69,via:232,view:[17,52,66,213],viewer:[56,208,216],virtualenv:65,visual:[207,240],vnc:56,vncserver:56,vncviewer:56,volum:69,wai:213,walk:184,walkgazemod:69,walktoobjectinimag:69,walktoobjectrayinworld:69,want:[10,231],warmstartcommand:69,waypoint:[17,19,69],waypointanchorhint:69,waypointsnapshot:69,waypointsourc:69,web:242,weight:[52,69],what:[10,14,17,31,215],when:[10,14,27],why:[10,17],width:52,wifi:55,wifidevic:69,wifiradiopowerst:69,wifist:69,wifistat:69,window:69,without:[204,225,231],workspace_arm_mov:32,workspacearmmoveparam:2,world:[41,45,140,202,212,243,244],world_object:69,world_object_servic:69,worldobject:69,worldobjectanchorhint:69,worldobjectservic:69,worldobjecttyp:69,would:10,wrench:69,wrenchtrajectori:69,wrenchtrajectorypoint:69,write:67,writer:[144,148,153,156,159],xbox:245,xyz:48,you:[17,65,231],your:65,zip:35,zxy:48}}) \ No newline at end of file diff --git a/docs/payload/configuring_payload_software.md b/docs/payload/configuring_payload_software.md index 03dd1f8f5..e3be88dbf 100644 --- a/docs/payload/configuring_payload_software.md +++ b/docs/payload/configuring_payload_software.md @@ -165,9 +165,9 @@ Devices on the payload network can reach the robot at 192.168.50.3 via port 443. | Description | Robot port | Target | Protocol | | ----------- | ----------- | ------ | -------- | | Standard Forwards 1 | 20000 + [22, 80, 443] | 192.168.50.5:[22, 80, 443] | TCP -| Fixed Forwards 1 | 21000-22000 | 192.158.50.5:21000-22000 | TCP/UDP +| Fixed Forwards 1 | 21000-22000 | 192.168.50.5:21000-22000 | TCP/UDP | Standard Forwards 2 | 30000 + [22, 80, 443] | 192.168.50.6:[22, 80, 443] | TCP -| Fixed Forwards 2 | 31000-32000 | 192.158.50.6:31000-32000 | TCP/UDP +| Fixed Forwards 2 | 31000-32000 | 192.168.50.6:31000-32000 | TCP/UDP Robot port forwards for ports 20443, 20022, 30443, and 30022 now masquerade. They should allow the payload to respond to forwarded traffic to the robot on any of the robot's ports. diff --git a/docs/payload/images/rails-image3.png b/docs/payload/images/rails-image3.png index 6181f7d95..81f96bd3a 100644 Binary files a/docs/payload/images/rails-image3.png and b/docs/payload/images/rails-image3.png differ diff --git a/docs/payload/images/rails3.png b/docs/payload/images/rails3.png index fa2abc1c9..b0931ee0b 100644 Binary files a/docs/payload/images/rails3.png and b/docs/payload/images/rails3.png differ diff --git a/docs/payload/spot_core_cockpit.md b/docs/payload/spot_core_cockpit.md index 1a96bf8b2..9aa72c960 100644 --- a/docs/payload/spot_core_cockpit.md +++ b/docs/payload/spot_core_cockpit.md @@ -76,7 +76,7 @@ nmcli d # List interface names. nmcli r wifi on # Turn WiFi radio on. nmcli d wifi list # List available WiFi networks. ``` -From the listed wireless access points, replace the captilized letters of the command below with your desired WiFi network and password. +From the listed wireless access points, replace the capitalized letters of the command below with your desired WiFi network and password. ``` sudo nmcli d wifi connect "MY_WIFI" password "MY_PASSWORD" ``` @@ -84,7 +84,7 @@ sudo nmcli d wifi connect "MY_WIFI" password "MY_PASSWORD" Included below is an example of running the above commands. ![login](./images/cockpit/terminal_wifi.png) -A succesful connection will appear as follows. +A successful connection will appear as follows. ![login](./images/cockpit/terminal_wifi_connected.png) @@ -104,7 +104,7 @@ In order to connect to the internet, remove the IPv4 default route of enp2s0. Se **IMPORTANT NOTES:** -1. By default, Boston Dynamics has included 2 pre-defined Routes which will route outbound communication from the Spot CORE to the explicity defined gateways, Spot's Access Point `192.168.80.0` and Spot's Ethernet port `10.0.0.0`, rather than the default gateway. +1. By default, Boston Dynamics has included 2 pre-defined Routes which will route outbound communication from the Spot CORE to the explicitly defined gateways, Spot's Access Point `192.168.80.0` and Spot's Ethernet port `10.0.0.0`, rather than the default gateway. 1. These routes are only required if the default gateway is removed for internet access. 1. The route to `10.0.0.0` is only valid if the user has not adjusted the Ethernet network settings on the robot admin console. 1. To access the internet, remove the default gateway (the third field) under Addresses as pictured below and Apply these changes. Instead of routing all traffic to the robot, we will instead route to the internet. diff --git a/docs/payload/spot_core_vnc.md b/docs/payload/spot_core_vnc.md index 7b64a0c78..75eb9b40f 100644 --- a/docs/payload/spot_core_vnc.md +++ b/docs/payload/spot_core_vnc.md @@ -133,7 +133,7 @@ vncserver -kill :15100 ``` ### Enable vncserver on boot -In order for vncserver to automatically run on Spot CORE whenever Spot is turned on, use a systemd .service file. Create a servive file named `vncserver@.service` and add the following contents. This file can take a port number as an argument. +In order for vncserver to automatically run on Spot CORE whenever Spot is turned on, use a systemd .service file. Create a service file named `vncserver@.service` and add the following contents. This file can take a port number as an argument. ``` [Unit] diff --git a/docs/python/fetch_tutorial/fetch3.md b/docs/python/fetch_tutorial/fetch3.md index 66ecddf75..d2bb2b21e 100644 --- a/docs/python/fetch_tutorial/fetch3.md +++ b/docs/python/fetch_tutorial/fetch3.md @@ -132,7 +132,7 @@ import threading from google.protobuf import wrappers_pb2 from object_detection.utils import label_map_util -kServiceAuthority = "auth.spot.robot" +kServiceAuthority = "fetch-tutorial-worker.spot.robot" @@ -578,7 +578,7 @@ python -m bosdyn.client 192.168.80.3 --username user --password YOUR_ROBOTS_PASS
    name                       type                                                      authority                             tokens
     --------------------------------------------------------------------------------------------------------------------------------
     [...]
    -fetch-server              bosdyn.api.NetworkComputeBridgeWorker                     auth.spot.robot                       user
    +fetch-server              bosdyn.api.NetworkComputeBridgeWorker                     fetch-tutorial-worker.spot.robot      user
     [...]
     
    diff --git a/docs/python/fetch_tutorial/fetch4.md b/docs/python/fetch_tutorial/fetch4.md index 665c8de40..bca5958aa 100644 --- a/docs/python/fetch_tutorial/fetch4.md +++ b/docs/python/fetch_tutorial/fetch4.md @@ -302,7 +302,7 @@ kImageSources = [ help='Person detection model name running on the external server.') parser.add_argument('-c', '--confidence-dogtoy', - help='Minimum confidence to return an object for the dogoy (0.0 to 1.0)', + help='Minimum confidence to return an object for the dogtoy (0.0 to 1.0)', default=0.5, type=float) parser.add_argument('-e', @@ -394,7 +394,7 @@ kImageSources = [


    - +
                print('Found dogtoy...')
     
                 # Got a dogtoy.  Request pick up.
    diff --git a/docs/python/fetch_tutorial/files/network_compute_server.py b/docs/python/fetch_tutorial/files/network_compute_server.py
    index 751577c41..ecc3b41ea 100644
    --- a/docs/python/fetch_tutorial/files/network_compute_server.py
    +++ b/docs/python/fetch_tutorial/files/network_compute_server.py
    @@ -30,7 +30,7 @@
     from google.protobuf import wrappers_pb2
     from object_detection.utils import label_map_util
     
    -kServiceAuthority = "auth.spot.robot"
    +kServiceAuthority = "fetch-tutorial-worker.spot.robot"
     
     
     class TensorFlowObjectDetectionModel:
    diff --git a/docs/python/quickstart.md b/docs/python/quickstart.md
    index e64fed6d7..ccdc732b3 100644
    --- a/docs/python/quickstart.md
    +++ b/docs/python/quickstart.md
    @@ -17,7 +17,7 @@ This guide will help you set up your programming environment to successfully com
          * [System Requirements](#system-requirements)
          * [Python Requirements](#python-requirements)
          * [Pip Installation](#pip-installation)
    -     * [Manage Multiple Python Environments with virtualenv](#manage-multiple-python-environments)
    +     * [Manage Multiple Python Environments with virtualenv](#manage-multiple-python-environments-with-virtualenv)
       * [Install Spot Python Packages](#install-spot-python-packages)
          * [Verify your Spot packages installation](#verify-your-spot-packages-installation)
       * [Verify you can command and query Spot](#verify-you-can-command-and-query-spot)
    @@ -121,8 +121,8 @@ Users with multiple python versions, anaconda, etc., are responsible for maintai
     
     ```
     $ python3 -m pip install virtualenv
    -$ python3 -m virtualenv my_spot_v2_0_env
    -$ source my_spot_v2_0_env/bin/activate
    +$ python3 -m virtualenv --python=/usr/bin/python3 my_spot_env
    +$ source my_spot_env/bin/activate
     $ (install packages including Spot SDK, code, edit, execute, etc.)
     ```
     
    @@ -132,25 +132,23 @@ To exit virtualenv...
     $ deactivate
     ```
     
    -**Note:** Please ensure the virtualenv was created using the expected version of python.  If you see:
    +**Note:** Please ensure the virtualenv was created and uses the correct version of python by
    +starting python after activating the virtual environment.
     
     ```shell
    -$ python -m virtualenv my_spot_v2_0_env
    -Running virtualenv with interpreter /usr/bin/python2
    -...
    -```
    -Then the wrong interpreter is being used.  You can pass the interpreter as an additional argument, for example:
    -
    -```shell
    -$ python -m virtualenv -p /usr/bin/python3 my_spot_v2_0_env
    +(my_spot_env) user@yourcomputer:~/user $ python
    +Python 3.6.9 (default, Oct  8 2020, 12:12:24)
    +[GCC 8.4.0] on linux
    +Type "help", "copyright", "credits" or "license" for more information.
    +>>>
     ```
     
     **Windows users:**
     
     ```shell
     > py.exe -3 -m pip install virtualenv
    -> py.exe -3 -m virtualenv my_spot_v2_0_env
    -> .\my_spot_v2_0_env\Scripts\activate.bat
    +> py.exe -3 -m virtualenv my_spot_env
    +> .\my_spot_env\Scripts\activate.bat
     > (install packages including Spot SDK, code, edit, execute, etc.)
     ```
     
    @@ -166,10 +164,10 @@ $ python3 -m pip install --upgrade bosdyn-client bosdyn-mission bosdyn-choreogra
     Installing the `bosdyn-client`, `bosdyn-choreography-client` and `bosdyn-mission` packages will also
     install `bosdyn-api` and `bosdyn-core` packages with the same version. The command above installs
     the latest version of the packages. To install a different version of the packages from PyPI, for
    -example 2.2.0, use the following command.
    +example 3.0.0, use the following command.
     
     ```shell
    -$ python3 -m pip install bosdyn-client==2.2.0 bosdyn-mission==2.2.0 bosdyn-choreography-client==2.2.0
    +$ python3 -m pip install bosdyn-client==3.0.0 bosdyn-mission==3.0.0 bosdyn-choreography-client==3.0.0
     ```
     
     **Version incompatibility:**
    @@ -178,7 +176,7 @@ If you see a version incompatibility error during pip install such as:
     
     ```shell
     ERROR: bosdyn-core  has requirement bosdyn-api==, but you
    -have bosdyn-api 2.2.0 which is incompatible.
    +have bosdyn-api 3.0.0 which is incompatible.
     ```
     
     Try uninstalling the bosdyn packages (Note: unlike install, you will need to explicitly list all 4 packages) and then reinstalling:
    @@ -193,12 +191,12 @@ Make sure that the packages have been installed.
     
     ```shell
     $ python3 -m pip list --format=columns | grep bosdyn
    -bosdyn-api                    2.3.5
    -bosdyn-choreography-client    2.3.5
    -bosdyn-choreography-protos    2.3.5
    -bosdyn-client                 2.3.5
    -bosdyn-core                   2.3.5
    -bosdyn-mission                2.3.5
    +bosdyn-api                    3.0.0
    +bosdyn-choreography-client    3.0.0
    +bosdyn-choreography-protos    3.0.0
    +bosdyn-client                 3.0.0
    +bosdyn-core                   3.0.0
    +bosdyn-mission                3.0.0
     ```
     **Windows users:**
     ```shell
    @@ -262,7 +260,7 @@ NOTE: The following examples will assume username "user" and password "password.
     
     ### Ping Spot
     
    -1. Power on Spot.  Wait for the fans to turn off (and maybe 10-20 seconds after that)
    +1. Power on Spot by holding the power button down until the fans start.  Wait for the fans to turn off (and maybe 10-20 seconds after that)
     2. Connect to Spot via wifi.
     3. Ping spot at 192.168.80.3
     
    @@ -284,12 +282,16 @@ Installed: 2020-12-11 15:06:57
     
     If this worked for you, SUCCESS!  You are now successfully communicating with Spot via Python!  Note that the output returned shows your Spot robot's unique serial number, its nickname and robot type (Boston Dynamics has multiple robots), the software version, and install date.
     
    -If you see the following:
    +If you see one of the following:
     
     ```shell
     $ python3 -m bosdyn.client 192.168.80.3 id
     Could not contact robot with hostname "192.168.80.3"
     ```
    +```shell
    +$ python3 -m bosdyn.client 192.168.80.3 id
    +RetryableUnavailableError: _InactiveRpcError: gRPC service unavailable. Likely transient and can be resolved by retrying the request.
    +```
     
     The robot is not powered on or is unreachable.  Go back and try to get your ping to work.  You can also try the `-v` or `--verbose` to get more information to debug the issue.
     
    @@ -325,15 +327,14 @@ $ python3 hello_spot.py --username user --password password 192.168.80.3
     Hello_spot will fail because there is not an E-Stop endpoint.
     
     ```shell
    -2020-03-30 15:26:36,283 - ERROR - Robot is E-Stopped. Please use an external E-Stop client, such as
    +2021-03-30 15:26:36,283 - ERROR - Robot is E-Stopped. Please use an external E-Stop client, such as
     the E-Stop SDK example, to configure E-Stop.
     ```
     
     If you see the following error:
     ```shell
     $ python3 hello_spot.py --username usehername --password pazwierd 192.168.80.3
    -2020-04-03 15:10:28,189 - ERROR - Hello, Spot! threw an exception: bosdyn.api.GetAuthTokenResponse:
    -Provided username/password is invalid.
    +2021-04-03 15:10:28,189 - ERROR - Hello, Spot! threw an exception: InvalidLoginError()
     ```
     
     Your username or password is incorrect. Check your spelling and verify your credentials with your robot administrator.
    diff --git a/docs/python/understanding_spot_programming.md b/docs/python/understanding_spot_programming.md
    index 1f3dbb7b9..1f181f73e 100644
    --- a/docs/python/understanding_spot_programming.md
    +++ b/docs/python/understanding_spot_programming.md
    @@ -370,37 +370,85 @@ Let's make a `LeaseClient` for the `lease` service and list the current leases:
     ```python
     >>> lease_client = robot.ensure_client('lease')
     >>> lease_client.list_leases()
    -[resource: "body"
    +[resource: "all-leases"
    +lease {
    +  resource: "all-leases"
    +  epoch: "ZBEwqFkjbTznJRxU"
    +  sequence: 1
    +  client_names: "root"
    +}
    +lease_owner {
    +}
    +, resource: "body"
     lease {
       resource: "body"
    -  epoch: "IOSDMpfEqvdTHZGV"
    -  sequence: 0
    +  epoch: "ZBEwqFkjbTznJRxU"
    +  sequence: 1
    +  client_names: "root"
    +}
    +lease_owner {
    +}
    +, resource: "mobility"
    +lease {
    +  resource: "mobility"
    +  epoch: "ZBEwqFkjbTznJRxU"
    +  sequence: 1
    +  client_names: "root"
     }
     lease_owner {
     }
     ]
    +
     ```
     
    -Lease-able resources are listed: currently the only resource supported is "body", which covers all of the motors on Spot.  Note that the `lease_owner` field is empty since no one has acquired the body lease.
    +The lease-able resources are listed: "body", "mobility", ("full-arm", "gripper", "arm" for robots with an arm). The "body" resource covers all of the motors on Spot and for most use-cases it is sufficient to issue all commands just using the "body" lease since services on Spot will break apart the lease to use the minimal set of resources necessary.
    +
    +NOTE: the `lease_owner` field is empty since no one has acquired the body lease.
     
    -Let's acquire a lease and again list:
    +When taking lease control of Spot, the lease should first be acquired, and then the "keepalive" should be created to retain ownership of the lease resource.
    +Let's acquire a lease, create the keepalive, and again list:
     
     ```python
    ->>> lease_keep_alive = bosdyn.client.lease.LeaseKeepAlive(lease_client)
     >>> lease = lease_client.acquire()
    +>>> lease_keep_alive = bosdyn.client.lease.LeaseKeepAlive(lease_client)
     >>> lease_client.list_leases()
    -[resource: "body"
    +[resource: "all-leases"
    +lease {
    +  resource: "all-leases"
    +  epoch: "ZBEwqFkjbTznJRxU"
    +  sequence: 2
    +  client_names: "root"
    +}
    +lease_owner {
    +  client_name: "HelloSpotClientlaptop-kbrandes01:hello_spot.py-32049"
    +}
    +, resource: "body"
     lease {
       resource: "body"
    -  epoch: "IOSDMpfEqvdTHZGV"
    -  sequence: 1
    +  epoch: "ZBEwqFkjbTznJRxU"
    +  sequence: 2
    +  client_names: "root"
     }
     lease_owner {
    -  client_name: "understanding-spotbblank02:29516"
    +  client_name: "HelloSpotClientlaptop-kbrandes01:hello_spot.py-32049"
    +}
    +, resource: "mobility"
    +lease {
    +  resource: "mobility"
    +  epoch: "ZBEwqFkjbTznJRxU"
    +  sequence: 2
    +  client_names: "root"
    +}
    +lease_owner {
    +  client_name: "HelloSpotClientlaptop-kbrandes01:hello_spot.py-32049"
     }
     ]
     ```
     
    +After acquiring the "body" lease, you take ownership of the sub-resource "mobility". For a robot with an arm, you will also take ownership of the sub-resources "full-arm", "arm", and "gripper" when acquiring the "body" lease.
    +
    +NOTE: the lease keepalive object must remain in scope for the entire duration of the program that is using the lease for commands.
    +
     ### Powering on the robot
     
     Now that you've authenticated to Spot, created an E-Stop endpoint, and acquired a lease, it's time to power on the robot.
    @@ -451,10 +499,10 @@ We encourage you to experiment with these various parameters, referencing the [r
     >>> from bosdyn.geometry import EulerZXY
     >>> footprint_R_body = EulerZXY(yaw=0.4, roll=0.0, pitch=0.0)
     >>> from bosdyn.client.robot_command import RobotCommandBuilder
    ->>> cmd = RobotCommandBuilder.stand_command(footprint_R_body=footprint_R_body)
    +>>> cmd = RobotCommandBuilder.synchro_stand_command(footprint_R_body=footprint_R_body)
     >>> command_client.robot_command(cmd)
     # Command Spot to raise up.
    ->>> cmd = RobotCommandBuilder.stand_command(body_height=0.1)
    +>>> cmd = RobotCommandBuilder.synchro_stand_command(body_height=0.1)
     >>> command_client.robot_command(cmd)
     ```
     
    diff --git a/docs/release_notes.md b/docs/release_notes.md
    index f592021c3..237350743 100644
    --- a/docs/release_notes.md
    +++ b/docs/release_notes.md
    @@ -12,6 +12,298 @@ Development Kit License (20191101-BDSDK-SL).
     
     # Spot Release Notes
     
    +## 3.0.0
    +
    +### New Features
    +
    +#### Graph Nav
    +
    +**Map Processing**
    +The new map processing service provides two ways to process the data in a graph nav map:
    +- adding new waypoints and edges to close loops and add connections in a map
    +- optimizing “anchorings” of a map, which will generate optimized positions of waypoints in the world for display or navigation.
    +
    +**Navigate to Anchor**
    +A new NaviagateToAnchor RPC can be used to command GraphNav to drive the robot to a specific place in an anchoring. GraphNav will find the waypoint that has the shortest path length from the robot's current position but is still close to the goal.
    +
    +See the [Graph Nav](concepts/autonomy/graphnav_map_structure.md) documentation for more information.
    +
    +#### Auto Return
    +
    +Auto Return is a service which can be configured to take control of the robot in the event of a communication loss, and return it back along its recently traveled route to attempt to regain communications with its user.  See the [Auto Return](concepts/autonomy/auto_return.md) documentation for more details.
    +
    +#### Choreography
    +
    +The Choreography API now provides 'choreography logging' which will capture timestamped data about the robot's pose and joint state for either a user defined time period or for the duration of a dance. See the [Choreography Service](concepts/choreography/choreography_service.md) documentation for more details.
    +
    +Users can now create animated moves using timestamped keyframes; these can be built through common animation software (like Autodesk Maya), handwritten, or constructed from choreography log data. The animated moves can be uploaded to the robot and used within dances like the base moves. See the [Animations Overview](concepts/choreography/animations_in_choreographer.md) documentation for more details.
    +
    +#### Constrained manipulation
    +
    +The constrained manipulation API provides the functionality to manipulate objects such as cranks, levers, cabinets and other similar objects with Spot. The API allows for the selection of a task type along with the desired task velocity to manipulate the object.
    +
    +#### Pushbar Door Opening
    +
    +API and tablet support for opening pushbar doors. The AutoPush API command takes a push point which the robot uses to detect the door and push it open at the specified location. See door.proto or arm_door.py for more details.
    +
    +#### SpotCam
    +
    +New SetPtzFocus and GetPtzFocus RPCs allow for control over the focus of the PTZ camera.
    +
    +#### Payloads
    +
    +Payload registration now supports mounting payloads to the wrist or gripper of the arm.  The new MountFrameName enum contains the valid mounting locations.
    +
    +A new UpdatePayloadAttached RPC allows for attaching and detaching payloads while the robot is operating.
    +
    +#### Missions
    +
    +Missions have a new `STATUS_STOPPED` state that can be triggered by the new `StopMission` rpc.  This state differs from a paused mission in that it means that the mission is no longer running and cannot be resumed.
    +
    +New mission node types:
    +- `BosdynNavigateRoute`: Use GraphNav via NavigateRoute
    +- `BosdynRecordEvent`: Record an API event in the data buffer.
    +- `SpotCamLed`: Change the brightnesses of the LEDs on a SpotCam.
    +- `SpotCamResetAutofocus`: Reset the autofocus on a SpotCam PTZ.
    +- `StoreMetadata`: Attach metadata to some data stored by a DataAcquisition node.
    +- `RetainLease`: Keep mission leases alive while the mission is running.  This allows the mission to run a larger variety of missions without requiring the mission service’s client to keep the lease alive.
    +- `RestartWhenPaused`: Restarts its child tree when a mission resumes, rather than resuming its child from the state it was in when it paused.
    +
    +#### Enable and Disable IR Emitters
    +
    +A new `IREnableDisable` service and request have been added.  This request allows clients to enable/disable the robot's IR light emitters in the body and hand sensors.  This new service supports special situations where Spot's emitters may interfere with a custom attached payload.  Disabling the IR emission will cause a SystemFault to be raised and have a negative effect on mobility since the robot's perception system is hindered.
    +
    +### Bug fixes and improvements
    +
    +#### Graph Nav
    +
    +For RPCs that can fail because the robot is impaired, the response message now includes a RobotImpairedState message providing details about the nature of the impairment.
    +
    +NavigationFeedbackStatus now includes body_movement_status to make it simple to determine when the body has come to rest after completing navigation.
    +
    +Directed exploration and alternate route finding can now be disabled using new fields in TravelParams.
    +
    +RouteFollowingParams have been added to NavigateRouteRequest to specify the desired behavior in certain situations (not starting from the start of the route, resuming a route, and becoming blocked on a route).
    +
    +New methods `navigate_to_full()` and `navigate_route_full()` has been added to the Graph Nav client which will return the full response instead of just the command id.
    +
    +Additional statuses have been added to NavigateRouteResponse (`STATUS_NO_PATH` and `STATUS_NOT_LOCALIZED_TO_MAP`) to capture possible errors.
    +
    +UploadGraph can fail with `STATUS_INVALID_GRAPH` when the specified graph is invalid, for example containing missing waypoints referenced by edges.
    +
    +DownloadWaypointSnapshotRequest has an additional option to not include point cloud data.
    +The new has_remote_point_cloud_sensor field in WaypointSnapshot indicates that the point has point cloud data from a remote service.
    +
    +Starting to record a new map can fail with the new status `STATUS_TOO_FAR_FROM_EXISTING_MAP`.
    +
    +The RPC for creating a new waypoint can now be provided a list of world objects to include in that waypoint’s snapshot.
    +
    +#### Missions
    +
    +For very large missions, a new RPC has been added to the mission service to stream the mission to the robot in chunks, rather than as a single message.  The chunks should still deserialize to the same LoadMissionRequest message when assembled.
    +
    +When a mission node fails to compile, the resulting FailedNode message has a new string that lists the protobuf type of the node implementation.
    +
    +#### Arm and Gripper Control
    +
    +We have improved the feedback for ArmJointMoveCommand Requests to now include the status of the underlying trajectory planner and to return the planner’s solved trajectory, which is the trajectory the robot will execute.
    +
    +The ManipulationFeedbackState contains extra enum values for additional placing states that the robot can be in during manipulation.
    +
    +By default, the robot will assume all grasped items are not “carriable”.  We have modified ApiGraspOverride to be able to override the carry state to one of `CARRIABLE`, `NOT_CARRIABLE`, or `CARRIABLE_AND_STOWABLE`.
    +
    +If holding an item, the stowing behavior is:
    +* `NOT_CARRIABLE` and `CARRIABLE` - The arm will not stow, instead it will stop
    +* `CARRIABLE_AND_STOWABLE` - The arm will stow while continuing to grasp the item
    +
    +In addition, the communication loss behavior of the arm when it is holding an item is also modified:
    +* `NOT_CARRIABLE` - The arm will release the item and stow
    +* `CARRIABLE` - The arm will not stow, instead entering stop
    +* `CARRIABLE_AND_STOWABLE` - The arm will stow while continuing to grasp the item
    +
    +#### Docking
    +
    +The docking state includes additional “in-between” states, `DOCK_STATUS_UNDOCKING` and `LINK_STATUS_DETECTING` that indicate that a process is still on-going.
    +Additional errors have been added to DockingCommandResponse for particular ways that docking can fail (`STATUS_ERROR_GRIPPER_HOLDING_ITEM`, `STATUS_ERROR_NOT_AVAILABLE`, `STATUS_ERROR_SYSTEM`).
    +The feedback also has a new error status: `STATUS_ERROR_NOT_AVAILABLE`.
    +See the [protobuf documentation](../protos/bosdyn/api/docking/docking.proto) for more details.
    +
    +The docking python client include a new `docking_command_full()` call which returns the full response instead of only the command id.  Additionally a new `docking_command_feedback_full()` returns the full feedback instead of only the status.
    +
    +#### Network Compute Bridge
    +
    +Models can now have a list of labels associated with them.
    +
    +When the image is requested to be rotated, the rotation angle will be returned in the response.
    +
    +#### Data Acquisition
    +
    +DataAcquisitionCapabilities can now also report the plugin service name of the service that will be performing that acquisition.
    +
    +The DataAcquisitionClient has a new `acquire_data_from_request()` that takes a full request proto, instead of building it internally.
    +
    +The DataService client has been updated to have the correct service name.
    +
    +#### Robot Commands
    +
    +Power commands can now report a `STATUS_OVERRIDDEN` if the robot overrides the power command and disables motor power.
    +
    +Power command responses and feedback will report system faults if a fault blocks the power command from succeeding.
    +
    +LeaseUseResults have been added to messages for RobotCommand and ManipulationApi
    +
    +New helpers `safe_power_off_robot()` and `safe_power_cycle_robot()` can completely power off the robot or power cycle the robot from any robot state.
    +
    +Additional options have been added to ObstacleParams for tuning obstacle avoidance by turning off negative obstacle avoidance or body assist for avoiding foot obstacles.
    +
    +#### Leases
    +
    +Leases represent ownership over the robot.  Leases have been updated to support ownership over only part of the robot, so that you can delegate control to different services, such as using Graph Nav to control robot mobility while simultaneously controlling the robot’s arm via a user-written script.  Details are in the [lease documentation](concepts/lease_service.md), but in general users can just continue to use the “body” lease and everything will work as expected.
    +
    +#### Other Changes
    +
    +The image service will now report `PixelFormat` for JPEG image sources even though the pixel format can be determined from the JPEG header.
    +
    +Spot Check has some additional failure statuses that it can report.
    +
    +Events logged to the data buffer may now include a LogPreserveHint to tell the robot whether this event is worth preserving a log for.
    +
    +RobotState now includes additional data about the terrain under each foot.
    +
    +Additional properties have been added to world objects to assist in image processing.
    +
    +An additional level of hierarchy has been added for transport-level errors to simplify most error cases. RpcError has two new subclasses: `PersistentRpcError` and `RetryableRpcError`.  `PersistentRpcError` indicates an error such that attempting to retry the call will fail again.  `RetryableRpcError` means that the call *may* succeed if retried.
    +
    +Calling `ensure_secure_channel()` directly will now result in max message sizes not being correctly.
    +
    +### Breaking Changes
    +
    +Invalid RobotCommands will no longer result in `STATUS_INVALID_REQUEST` in the RobotCommandResponse message, but will instead use the `CODE_INVALID_REQUEST` error in the common header, like other RPCs do.  In the python client library, this will still raise the same `InvalidRequestError` as before.
    +
    +E-stops may not be unregistered from the estop service while the robot’s motors are powered. This prevents accidentally powering the robot off. A new `STATUS_MOTORS_ON` status will be returned in the response to indicate this error. To unregister an estop, first safely power off the robot.
    +
    +RPCs to the AuthService and PayloadRegistration service are now rate-limited to 5 and 10 requests/second respectively.  Requesting more than that will result in an HTTP 429 error, or raising the `TooManyRequestsError` if using the python client.
    +
    +The “obstacle_distance” local grid inadvertently included some generated obstacles used only for foot-placement control.  These grids no longer include those generated obstacle regions.
    +
    +The python function `bosdyn.client.lease.test_active_lease` previously took an optional `make_sublease` argument.  That has been replaced with an optional `sublease_name` argument so that if a sublease is desired, it gets created with a client name correctly.
    +
    +The StraightStaircase message has been moved to bosdyn/api/stairs.proto so that it can be used in more places.  This is compatible with existing serialized protobufs, but any code that is manually creating these messages will need to be updated.
    +
    +### Deprecations
    +
    +#### Robot Control
    +
    +The `enable_grated_floor` field is superceded by the new `grated_surfaces_mode` which will auto-detect the need for grated surface handling.
    +
    +The `safe_power_off()` helper has been replaced by the less ambiguous `safe_power_off_motors()` helper.
    +
    +The `docking_command_feedback()` method of DockingClient incorrectly raised an exception if the docking command had encountered lease errors, and has been replaced by `docking_command_feedback_full()` which returns the full feedback response.
    +
    +#### Graph Nav
    +
    +For limiting the speed on an edge, use the `vel_limit` in `mobility_params` instead of the Edge annonation’s `vel_limit`.
    +
    +#### Missions
    +
    +Docking nodes should not use the child node anymore.  If a mission needs to react to docking results, it should use the responses written into the blackboard by the docking node.
    +
    +#### Payloads
    +
    +The `guid` and `secret` fields on payload registration RPCs have been replaced with a standardized `PayloadCredentials` message.
    +
    +#### Writing services
    +
    +Many helpers for writing services and filling out responses have been moved from `bosdyn.client.util` to `bosdyn.client.server_util`.
    +
    +`bosdyn.mission.server_util.set_response_header()` has been moved to `bosdyn.client.server_util.set_response_header()`, so that it can be used by any service, not only those depending on missions.
    +
    +### Known Issues
    +
    +**When a network transport failure occurs,** depending on the particular operating system and version of gRPC installed, the error from the python SDK may not always be the most specific error possible, such as `UnknownDnsNameError`.  It may instead be raised as either a generic `RpcError`, or another generic failure type such as `UnableToConnectToRobotError`.
    +
    +**Spot CAM LED illumination levels** are not currently recorded or played back in Autowalk missions.
    +
    +**If you write a custom data acquisition plugin or image service,** do not change its `DataAcquisitionCapability` or `ImageSource` set once it is running and registered. New capabilities may not be detected, and old capabilities may still be listed as available in the Data Acquisition service. To change the capabilities of a service: unregister it from the directory, wait until its capabilities are no longer listed in the Data Acquisition service, and then re-register it. This waiting also applies to restarting a service if its capabilities will be different upon restart.
    +
    +**If you write a custom data acquisition plugin without using our helper class,** its `GetStatus()` rpc is expected to complete immediately. If it takes too long to complete it can cause timeouts when requesting `GetStatus()` of the data acquisition service.
    +
    +**If you register a new service with the robot**, calling `robot.ensure_client()` to create a client for that service may result in a `UnregisteredServiceNameError`.
    +
    +  * Workaround: call `robot.sync_with_directory()` before `robot.ensure_client()`
    +
    +**SE2VelocityLimits require care**.  Correct usage of the `SE2VelocityLimit` message requires the user to fully fill out all the fields, setting unlimited values to a large number, say 1e6.
    +
    +### Sample Code
    +
    +[**Animation Recorder (new)**](../python/examples/animation_recorder/README.md)
    +Demonstrates recording motion made with the tablet and then playing it back with the choreographer service.
    +
    +[**Arm Constrained Manipulation (new)**](../python/examples/arm_constrained_manipulation/README.md)
    +Demonstrates using constrained manipulation to turn a crank.
    +
    +[**Auto Return (new)**](../python/examples/auto_return/README.md)
    +Demonstrates setting up and triggering the Auto Return service.
    +
    +[**Data Buffer (new)**](../python/examples/data_buffer/README.md)
    +Demonstrates adding several different kinds of data to the data buffer.
    +
    +[**Get Depth Plus Visual Image (new)**](../python/examples/get_depth_plus_visual_image/README.md)
    +Demonstrates how to use the `depth_in_visual_frame` image sources to visualize depth in a visual image.
    +
    +[**Graph Nav Extract Point Cloud (new)**](../python/examples/graph_nav_extract_point_cloud/README.md)
    +Demonstrates opening and parsing a GraphNav map and extracting a globally consistent point cloud from it.
    +
    +[**Post-Docking Callbacks (new)**](../python/examples/post_docking_callbacks/README.md)
    +Builds docker images for mission callbacks that can upload data acquired during a mission.
    +
    +[**Disable IR Emission (new)**](../python/examples/disable_ir_emission/README.md)
    +Demonstrates enabling and disabling IR light emitters via the IREnableDisableService Client.
    +
    +[**Arm Gaze (updated)**](../python/examples/arm_gaze/README.md)
    +Updated to use `block_until_arm_arrives()` instead of a `sleep()`
    +
    +[**BDDF Download (updated)**](../python/examples/bddf_download/README.md)
    +Now provides a Qt-based downloading UI.
    +
    +[**Data Acquisition Service (updated)**](../python/examples/data_acquisition_service/README.md)
    +Now provides a plugin for capturing data from the network compute bridge.
    +
    +[**Frame Trajectory Command (updated)**](../python/examples/frame_trajectory_command/README.md)
    +Now takes arguments that specify how to move instead of performing fixed motions.
    +
    +[**Graph Nav Command Line (updated)**](../python/examples/graph_nav_command_line/README.md)
    +Now can navigate to a position in an anchoring.  The recording example provides options to automatically close loops in the map or optimize the map's anchoring.
    +
    +[**View Map (updated)**](../python/examples/graph_nav_view_map/README.md)
    +Now can display the map according to the anchoring.
    +
    +[**Mission Recorder (updated)**](../python/examples//README.md)
    +New option to build a mission from the graph on the robot.
    +New command to automatically close loops in the graph.
    +Uses NavigateRoute to follow waypoints in order.
    +
    +[**Network Compute Bridge (updated)**](../python/examples/network_compute_bridge/README.md)
    +Now includes options to run and test a worker without needing a robot.  Also includes files to build docker images for deployment.
    +
    +[**Payloads (updated)**](../python/examples/payloads/README.md)
    +Now includes an example of how to attach and detach a payload.
    +
    +[**Replay Mission (updated)**](../python/examples/replay_mission/README.md)
    +Now includes extra options for playing back Autowalk missions, as well as disabling directed exploration.
    +
    +[**Ricoh Theta (updated)**](../python/examples/ricoh_theta/README.md)
    +Now includes a "live stream" option that provides a higher frame rate at the cost of lower-quality stitching. (Thanks Aaron Gokasian!)
    +
    +[**Spot Cam (updated)**](../python/examples/spot_cam/README.md)
    +New options for getting and setting PTZ focus, audio capture channel, and audio capture gain.  Also an option for enabling congestion control for the stream quality.
    +
    +[**WASD (updated)**](../python/examples/wasd/README.md)
    +The Escape key can now be used to stop the robot.
    +
    +[**Webcam Image Service (updated)**](../python/examples/web_cam_image_service/README.md)
    +New resolution options allow for capturing from the webcam at different resolutions.
    +
     ## 2.3.5
     
     ### New Features
    @@ -57,7 +349,7 @@ These new options will only work on some Enterprise Spot robots. Check the Hardw
     
     ### Bug fixes and improvements
     
    -Fixed an issue that could cause payload registration or directory registraion keep-alive threads to exit early in certain cases.
    +Fixed an issue that could cause payload registration or directory registration keep-alive threads to exit early in certain cases.
     
     Fixed a couple issues with the webcam example: updated the Dockerfile to create a smaller container specifically with python 3.7, added new optional argument to specify the video codec, and programmatically prevent substring arguments other than the `--device-name` argument to avoid accidental confusion with the docker container's `--device` argument.
     
    @@ -212,7 +504,7 @@ All rpcs in the Python SDK have a default timeout of 30s.  The global timeout ca
     
     **When capturing both a PTZ and Panoramic image** in the same action, there may occasionally be two PTZ images captured along with the Panoramic image, rather than just one.
     
    -**If you write a custom data acquisition plugin or image service,** do not change its `DataAcquisitionCapability` or `ImageSource` set once it is running and registered. New capabilities may not be detected, and old capababilities may still be listed as available in the Data Acquisition service. To change the capabilities of a service: unregister it from the directory, wait until its capabilities are no longer listed in the Data Acquisition service, and then re-register it. This waiting also applies to restarting a service if its capabilities will be different upon restart.
    +**If you write a custom data acquisition plugin or image service,** do not change its `DataAcquisitionCapability` or `ImageSource` set once it is running and registered. New capabilities may not be detected, and old capabilities may still be listed as available in the Data Acquisition service. To change the capabilities of a service: unregister it from the directory, wait until its capabilities are no longer listed in the Data Acquisition service, and then re-register it. This waiting also applies to restarting a service if its capabilities will be different upon restart.
     
     **If you write a custom data acquisition plugin without using our helper class,** its `GetStatus()` rpc is expected to complete immediately. If it takes too long to complete it can cause timeouts when requesting `GetStatus()` of the data acquisition service.
     
    diff --git a/prebuilt/bosdyn_api-2.3.5-py2.py3-none-any.whl b/prebuilt/bosdyn_api-2.3.5-py2.py3-none-any.whl
    deleted file mode 100644
    index 581bf75cf..000000000
    Binary files a/prebuilt/bosdyn_api-2.3.5-py2.py3-none-any.whl and /dev/null differ
    diff --git a/prebuilt/bosdyn_api-3.0.0-py2.py3-none-any.whl b/prebuilt/bosdyn_api-3.0.0-py2.py3-none-any.whl
    new file mode 100644
    index 000000000..c79861e59
    Binary files /dev/null and b/prebuilt/bosdyn_api-3.0.0-py2.py3-none-any.whl differ
    diff --git a/prebuilt/bosdyn_choreography_client-2.3.5-py2.py3-none-any.whl b/prebuilt/bosdyn_choreography_client-2.3.5-py2.py3-none-any.whl
    deleted file mode 100644
    index 6da49d26e..000000000
    Binary files a/prebuilt/bosdyn_choreography_client-2.3.5-py2.py3-none-any.whl and /dev/null differ
    diff --git a/prebuilt/bosdyn_choreography_client-3.0.0-py2.py3-none-any.whl b/prebuilt/bosdyn_choreography_client-3.0.0-py2.py3-none-any.whl
    new file mode 100644
    index 000000000..7f24fbeda
    Binary files /dev/null and b/prebuilt/bosdyn_choreography_client-3.0.0-py2.py3-none-any.whl differ
    diff --git a/prebuilt/bosdyn_choreography_protos-2.3.5-py3-none-any.whl b/prebuilt/bosdyn_choreography_protos-2.3.5-py3-none-any.whl
    deleted file mode 100644
    index 9954ed592..000000000
    Binary files a/prebuilt/bosdyn_choreography_protos-2.3.5-py3-none-any.whl and /dev/null differ
    diff --git a/prebuilt/bosdyn_choreography_protos-3.0.0-py3-none-any.whl b/prebuilt/bosdyn_choreography_protos-3.0.0-py3-none-any.whl
    new file mode 100644
    index 000000000..14b325fac
    Binary files /dev/null and b/prebuilt/bosdyn_choreography_protos-3.0.0-py3-none-any.whl differ
    diff --git a/prebuilt/bosdyn_client-2.3.5-py2.py3-none-any.whl b/prebuilt/bosdyn_client-2.3.5-py2.py3-none-any.whl
    deleted file mode 100644
    index 5a93b5371..000000000
    Binary files a/prebuilt/bosdyn_client-2.3.5-py2.py3-none-any.whl and /dev/null differ
    diff --git a/prebuilt/bosdyn_client-3.0.0-py2.py3-none-any.whl b/prebuilt/bosdyn_client-3.0.0-py2.py3-none-any.whl
    new file mode 100644
    index 000000000..80e4ea54e
    Binary files /dev/null and b/prebuilt/bosdyn_client-3.0.0-py2.py3-none-any.whl differ
    diff --git a/prebuilt/bosdyn_core-2.3.5-py2.py3-none-any.whl b/prebuilt/bosdyn_core-2.3.5-py2.py3-none-any.whl
    deleted file mode 100644
    index dc1b55e0d..000000000
    Binary files a/prebuilt/bosdyn_core-2.3.5-py2.py3-none-any.whl and /dev/null differ
    diff --git a/prebuilt/bosdyn_core-3.0.0-py2.py3-none-any.whl b/prebuilt/bosdyn_core-3.0.0-py2.py3-none-any.whl
    new file mode 100644
    index 000000000..bfaaa3741
    Binary files /dev/null and b/prebuilt/bosdyn_core-3.0.0-py2.py3-none-any.whl differ
    diff --git a/prebuilt/bosdyn_mission-2.3.5-py2.py3-none-any.whl b/prebuilt/bosdyn_mission-2.3.5-py2.py3-none-any.whl
    deleted file mode 100644
    index ee55868e5..000000000
    Binary files a/prebuilt/bosdyn_mission-2.3.5-py2.py3-none-any.whl and /dev/null differ
    diff --git a/prebuilt/bosdyn_mission-3.0.0-py2.py3-none-any.whl b/prebuilt/bosdyn_mission-3.0.0-py2.py3-none-any.whl
    new file mode 100644
    index 000000000..05e2b1cf0
    Binary files /dev/null and b/prebuilt/bosdyn_mission-3.0.0-py2.py3-none-any.whl differ
    diff --git a/protos/bosdyn/api/arm_command.proto b/protos/bosdyn/api/arm_command.proto
    index adabb93a3..a2e4cf622 100644
    --- a/protos/bosdyn/api/arm_command.proto
    +++ b/protos/bosdyn/api/arm_command.proto
    @@ -128,7 +128,7 @@ message ArmVelocityCommand {
             // The angular velocity of the hand frame measured with respect to the odom frame, expressed
             // in the hand frame. A 'X' rate will cause the hand to rotate about its x-axis, e.g. the
             // final wrist twist joint will rotate. And similarly, 'Y' and 'Z' rates will cause the hand
    -        // to rotate about its y and z axis respectively. \ 
    +        // to rotate about its y and z axis respectively. \
             // The units should be rad/sec.
             Vec3 angular_velocity_of_hand_rt_odom_in_hand = 6;
     
    @@ -162,7 +162,8 @@ message NamedArmPositionsCommand {
             POSITIONS_READY = 2;
     
             // Stow the arm, safely. If the robot is holding something, it will freeze the arm instead
    -        // of stowing.
    +        // of stowing.  Overriding the carry_state to CARRY_STATE_CARRIABLE_AND_STOWABLE, will allow
    +        // the robot to stow the arm while grasping an item.
             POSITIONS_STOW = 3;
         }
     
    @@ -313,7 +314,7 @@ message ArmJointMoveCommand {
     
         message Feedback {
             enum Status {
    -            // STATUS_UNKNOWN should never be used. If used, an internal error has happened.
    +            // STATUS_UNKNOWN should never be used. If used, an internal error has happened
                 STATUS_UNKNOWN = 0;
                 // The arm is at the desired configuration.
                 STATUS_COMPLETE = 1;
    @@ -322,6 +323,40 @@ message ArmJointMoveCommand {
             }
             // Current status of the request.
             Status status = 1;
    +    
    +        enum PlannerStatus {
    +            // PLANNER_STATUS_UNKNOWN should never be used. If used, an internal error has happened.
    +            PLANNER_STATUS_UNKNOWN = 0;
    +            // A solution passing through the desired points and obeying the constraints was found.
    +            PLANNER_STATUS_SUCCESS = 1;
    +            // The planner had to modify the desired points in order to obey the constraints.  For
    +            // example, if you specify a 1 point trajectory, and tell it to get there in a really short
    +            // amount of time, but haven't set a high allowable max velocity / acceleration, the planner
    +            // will do its best to get as close as possible to the final point, but won't reach it. In
    +            // situations where we've modified you last point, we append a minimum time trajectory (that
    +            // obeys the velocity and acceleration limits) from the planner's final point to the requested
    +            // final point.
    +            PLANNER_STATUS_MODIFIED = 2;
    +            // Failed to compute a valid trajectory, will go to first point instead. It is possible
    +            // that our optimizer till fail to solve the problem instead of returning a sub-optimal
    +            // solution.  This is un-likely to occur.
    +            PLANNER_STATUS_FAILED = 3;
    +        }
    +        // Current status of the trajectory planner.
    +        PlannerStatus planner_status = 2;
    +
    +        // Based on the user trajectory, the planned knot points that obey acceleration and 
    +        // velocity constraints. If these knot points don't match the requested knot points, 
    +        // consider increasing velocity/acceleration limits, and/or staying further away from 
    +        // joint position limits. In situations where we've modified you last point, we append
    +        // a minimum time trajectory (that obeys the velocity and acceleration limits) from the
    +        // planner's final point to the requested final point. This means that the length of
    +        // planned_points may be one point larger than the requested.
    +        repeated ArmJointTrajectoryPoint planned_points = 3;
    +
    +        // Returns amount of time remaining until the joints are at the goal position.  For
    +        // multiple point trajectories, this is the time remaining to the final point.
    +        google.protobuf.Duration time_to_goal = 4;
         }
     }
     
    @@ -401,16 +436,15 @@ message GazeCommand {
             Vec3Trajectory target_trajectory_in_frame1 = 1;
             string frame1_name = 2;
     
    -        // Optional desired pose of the tool expressed in frame2.  Will be constrained to 'look at'
    +        // Optional, desired pose of the tool expressed in frame2.  Will be constrained to 'look at'
             // the target regardless of the requested orientation.
             SE3Trajectory tool_trajectory_in_frame2 = 10;
             string frame2_name = 11;
     
    -        // The tool pose relative to the parent link (wrist).
    -        // Defaults to the gripper's hand frame position with the gripper camera's
    -        // orientation.
    -        //    pos: [0.195570  0  0]
    -        //    rot: [0.9969173 0 -0.0784593 0] (-9 degrees about y)
    +        // The transformation of the tool pose relative to the parent link (wrist).
    +        // If the field is left unset, the transform will default to:
    +        //      The position is wrist_tform_hand.position() [20 cm translation in wrist x].
    +        //      The rotation is wrist_tform_hand_camera.rotation() [-9 degree pitch about wrist y].
             SE3Pose wrist_tform_tool = 9;
     
             // Optional velocity to move the target along the shortest path from the gaze's starting
    diff --git a/protos/bosdyn/api/auto_return/auto_return.proto b/protos/bosdyn/api/auto_return/auto_return.proto
    new file mode 100644
    index 000000000..7a5f5421e
    --- /dev/null
    +++ b/protos/bosdyn/api/auto_return/auto_return.proto
    @@ -0,0 +1,98 @@
    +// Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +//
    +// Downloading, reproducing, distributing or otherwise using the SDK Software
    +// is subject to the terms and conditions of the Boston Dynamics Software
    +// Development Kit License (20191101-BDSDK-SL).
    +
    +syntax = "proto3";
    +
    +package bosdyn.api.auto_return;
    +option java_outer_classname = "AutoReturnProto";
    +
    +import "google/protobuf/any.proto";
    +import "google/protobuf/duration.proto";
    +
    +import "bosdyn/api/header.proto";
    +import "bosdyn/api/lease.proto";
    +
    +// Parameters to AutoReturn actions.
    +message Params {
    +    // Robot-specific mobility parameters to use.
    +    // For example, see bosdyn.api.spot.MobilityParams.
    +    google.protobuf.Any mobility_params = 1;
    +
    +    // Allow AutoReturn to move the robot this far in the XY plane from the location where
    +    // AutoReturn started. Travel along the Z axis (which is gravity-aligned) does not count.
    +    // Must be > 0.
    +    float max_displacement = 2; // meters
    +
    +    // Optionally specify the maximum amount of time AutoReturn can take.
    +    // If this duration is exceeded, AutoReturn will stop trying to move the robot.
    +    // Omit to try indefinitely. Robot may become stuck and never take other comms loss actions!
    +    google.protobuf.Duration max_duration = 3;
    +}
    +
    +// Configure the AutoReturn system.
    +message ConfigureRequest {
    +    // Common request header.
    +    RequestHeader header = 1;
    +
    +    // Leases that AutoReturn may use to accomplish its goals when AutoReturn automatically
    +    // triggers. If left empty, AutoReturn will not automatically trigger.
    +    repeated Lease leases = 2;
    +
    +    // Parameters to use when AutoReturn automatically triggers.
    +    Params params = 3;
    +    
    +    // Forget any buffered locations the robot may be remembering. Defaults to false.
    +    // Set to true if the robot has just crossed an area it should not traverse in AutoReturn.
    +    bool clear_buffer = 4;
    +
    +}
    +
    +// Response to a configuration request.
    +message ConfigureResponse {
    +    // Common response header.
    +    ResponseHeader header = 1;
    +
    +    enum Status {
    +        STATUS_UNKNOWN = 0;
    +        STATUS_OK = 1;
    +        STATUS_INVALID_PARAMS = 2;
    +    }
    +    // Return status for the request.
    +    Status status = 2;
    +
    +    // If status is STATUS_INVALID_PARAMS, this contains the settings that were invalid.
    +    Params invalid_params = 3;
    +}
    +
    +// Ask for the current configuration.
    +message GetConfigurationRequest {
    +    // Common request header.
    +    RequestHeader header = 1;
    +}
    +
    +// Response to a "get configuration" request.
    +message GetConfigurationResponse {
    +    // Common response header.
    +    ResponseHeader header = 1;
    +
    +    // A simple yes/no, will AutoReturn automatically trigger.
    +    bool enabled = 2;
    +
    +    // The most recent successful ConfigureRequest.
    +    // Will be empty if service has not successfully been configured.
    +    ConfigureRequest request = 3;
    +}
    +
    +// Start AutoReturn behavior now.
    +message StartRequest {
    +    // Common request header.
    +    RequestHeader header = 1;
    +}
    +
    +message StartResponse {
    +    // Common response header.
    +    ResponseHeader header = 1;
    +}
    diff --git a/protos/bosdyn/api/auto_return/auto_return_service.proto b/protos/bosdyn/api/auto_return/auto_return_service.proto
    new file mode 100644
    index 000000000..fff8a21b7
    --- /dev/null
    +++ b/protos/bosdyn/api/auto_return/auto_return_service.proto
    @@ -0,0 +1,25 @@
    +// Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +//
    +// Downloading, reproducing, distributing or otherwise using the SDK Software
    +// is subject to the terms and conditions of the Boston Dynamics Software
    +// Development Kit License (20191101-BDSDK-SL).
    +
    +syntax = "proto3";
    +
    +package bosdyn.api.auto_return;
    +
    +option java_outer_classname = "AutoReturnServiceProto";
    +
    +import "bosdyn/api/auto_return/auto_return.proto";
    +
    +service AutoReturnService {
    +
    +    // Configure the service.
    +    rpc Configure(ConfigureRequest) returns (ConfigureResponse);
    +
    +    // Get the current configuration.
    +    rpc GetConfiguration(GetConfigurationRequest) returns (GetConfigurationResponse);
    +
    +    // Start AutoReturn now.
    +    rpc Start(StartRequest) returns (StartResponse);
    +}
    diff --git a/protos/bosdyn/api/basic_command.proto b/protos/bosdyn/api/basic_command.proto
    index a6d97c770..22a3d1ea0 100644
    --- a/protos/bosdyn/api/basic_command.proto
    +++ b/protos/bosdyn/api/basic_command.proto
    @@ -13,6 +13,7 @@ option java_outer_classname = "BasicCommandProto";
     import "bosdyn/api/geometry.proto";
     import "bosdyn/api/trajectory.proto";
     import "google/protobuf/timestamp.proto";
    +import "google/protobuf/wrappers.proto";
     
     message RobotCommandFeedbackStatus {
         enum Status {
    @@ -231,6 +232,7 @@ message SitCommand {
     message StandCommand {
         message Request {
             // Stand command request takes no additional arguments.
    +
         }
     
         message Feedback {
    @@ -339,3 +341,133 @@ message ArmDragCommand {
             Status status = 1;
         }
     }
    +
    +
    +message ConstrainedManipulationCommand {
    +    message Request {
    +
    +        // Frame that the initial wrench will be expressed in
    +        string frame_name = 1;
    +        // Direction of the initial wrench to be applied
    +        // Depending on the task, either the force vector or the
    +        // torque vector are required to be specified. The required
    +        // vector should not have a magnitude of zero and will be
    +        // normalized to 1. For tasks that require the force vector,
    +        // the torque vector can still be specified as a non-zero vector
    +        // if it is a good guess of the axis of rotation of the task.
    +        // (for e.g. TASK_TYPE_SE3_ROTATIONAL_TORQUE task types.)
    +        // Note that if both vectors are non-zero, they have to be perpendicular.
    +        // Once the constrained manipulation system estimates the
    +        // constraint, the init_wrench_direction and frame_name
    +        // will no longer be used.
    +        bosdyn.api.Wrench init_wrench_direction_in_frame_name = 2;
    +
    +        // The desired velocity to move the object
    +        // For all tasks besides SE3_ROTATIONAL_TORQUE, set
    +        // tangential_speed in units of m/s. For SE3_ROTATIONAL_TORQUE,
    +        // set rotational_speed with units of rad/s.
    +        oneof task_speed {
    +            // Recommended values are in the range of [-4, 4] m/s
    +            double tangential_speed = 3;
    +            // Recommended values are in the range of [-4, 4] rad/s
    +            double rotational_speed = 4;
    +        }
    +
    +        // The limit on the force that is applied on any translation direction
    +        // Value must be positive
    +        // If unspecified, a default value of 40 N will be used.
    +        google.protobuf.DoubleValue force_limit = 5;
    +        // The limit on the torque that is applied on any rotational direction
    +        // Value must be positive
    +        // If unspecified, a default value of 4 Nm will be used.
    +        google.protobuf.DoubleValue torque_limit = 6;
    +
    +        // Geometrical category of a task. See the constrained_manipulation_helper function
    +        // for examples of each of these categories. For e.g. SE3_CIRCLE_FORCE_TORQUE corresponds
    +        // to lever type objects.
    +        enum TaskType {
    +            TASK_TYPE_UNKNOWN = 0;
    +            // This task type corresponds to circular tasks where
    +            // both the end-effector position and orientation rotate about a circle to manipulate.
    +            // The constrained manipulation logic will generate forces and torques in this case.
    +            // Example tasks are: A lever or a ball valve with a solid grasp
    +            // This task type will require an initial force vector specified
    +            // in init_wrench_direction_in_frame_name. A torque vector can be specified
    +            // as well if a good initial guess of the axis of rotation of the task is available.
    +            TASK_TYPE_SE3_CIRCLE_FORCE_TORQUE = 1;
    +            // This task type corresponds to circular tasks that have an extra degree of freedom.
    +            // In these tasks the end-effector position rotates about a circle
    +            // but the orientation does not need to follow a circle (can remain fixed).
    +            // The constrained manipulation logic will generate translational forces in this case.
    +            // Example tasks are: A crank that has a loose handle and moves in a circle
    +            // and the end-effector is free to rotate about the handle in one direction.
    +            // This task type will require an initial force vector specified
    +            // in init_wrench_direction_in_frame_name.
    +            TASK_TYPE_R3_CIRCLE_EXTRADOF_FORCE = 2;
    +            // This task type corresponds to purely rotational tasks.
    +            // In these tasks the orientation of the end-effector follows a circle,
    +            // and the position remains fixed. The robot will apply a torque at the
    +            // end-effector in these tasks.
    +            // Example tasks are: rotating a knob or valve that does not have a lever arm.
    +            // This task type will require an initial torque vector specified
    +            // in init_wrench_direction_in_frame_name.
    +            TASK_TYPE_SE3_ROTATIONAL_TORQUE = 3;
    +            // This task type corresponds to circular tasks where
    +            // the end-effector position and orientation rotate about a circle
    +            // but the orientation does always strictly follow the circle due to slips.
    +            // The constrained manipulation logic will generate translational forces in this case.
    +            // Example tasks are: manipulating a cabinet where the grasp on handle is not very rigid
    +            // or can often slip.
    +            // This task type will require an initial force vector specified
    +            // in init_wrench_direction_in_frame_name.
    +            TASK_TYPE_R3_CIRCLE_FORCE = 4;
    +            // This task type corresponds to linear tasks where
    +            // the end-effector position moves in a line
    +            // but the orientation does not need to change.
    +            // The constrained manipulation logic will generate a force in this case.
    +            // Example tasks are: A crank that has a loose handle, or manipulating
    +            // a cabinet where the grasp of the handle is loose and the end-effector is free
    +            // to rotate about the handle in one direction.
    +            // This task type will require an initial force vector specified
    +            // in init_wrench_direction_in_frame_name.
    +            TASK_TYPE_R3_LINEAR_FORCE = 5;
    +            // This option simply holds the hand in place with stiff impedance control.
    +            // You can use this mode at the beginning of a constrained manipulation task or to
    +            // hold position while transitioning between two different constrained manipulation tasks.
    +            // The target pose to hold will be the measured hand pose upon transitioning to constrained
    +            // manipulation or upon switching to this task type. 
    +            // This mode should only be used during constrained manipulation tasks,
    +            // since it uses impedance control to hold the hand in place.
    +            // This is not intended to stop the arm during position control moves.
    +            TASK_TYPE_HOLD_POSE = 6;
    +        }
    +        TaskType task_type = 7;
    +
    +        // The timestamp (in robot time) by which a command must finish executing.
    +        // This is a required field and used to prevent runaway commands.
    +        google.protobuf.Timestamp end_time = 8;
    +
    +    }
    +
    +    message Feedback {
    +        enum Status {
    +            // STATUS_UNKNOWN should never be used. If used, an internal error has happened.
    +            STATUS_UNKNOWN = 0;
    +            // Constrained manipulation is working as expected
    +            STATUS_RUNNING = 1;
    +            // Arm is stuck, either force is being applied in a direction
    +            // where the affordance can't move or not enough force is applied
    +            STATUS_ARM_IS_STUCK = 2;
    +            // The grasp was lost. In this situation, constrained manipulation
    +            // will stop applying force, and will hold the last position.
    +            STATUS_GRASP_IS_LOST = 3;
    +        }
    +        Status status = 1;
    +
    +        // Desired wrench in odom world frame, applied at hand frame origin
    +        bosdyn.api.Wrench desired_wrench_odom_frame = 2;
    +
    +        // Add a signal indicating that constrained manipulation is using
    +        // its estimated direction for applying wrench.
    +    }
    +}
    diff --git a/protos/bosdyn/api/data_acquisition.proto b/protos/bosdyn/api/data_acquisition.proto
    index 05d03133c..852c909b5 100644
    --- a/protos/bosdyn/api/data_acquisition.proto
    +++ b/protos/bosdyn/api/data_acquisition.proto
    @@ -29,6 +29,9 @@ message DataAcquisitionCapability {
         // Channel name that will be associated with all data stored in the data buffer through
         // each data acquisition plugin. Metadata acquirers do not specify this field.
         string channel_name = 3;
    +
    +    // The data acquisition plugin service's service name used in directory registration.
    +    string service_name = 4;
     }
     
     // Description of an image acquisition capability. The image acquisition capabilities will be available
    @@ -98,6 +101,7 @@ message Metadata {
     // metadata that is associated with previously stored data.
     message AssociatedMetadata {
         // The data that this metadata refers to.
    +    // The timestamp field is ignored.
         // If only the action_id is filled out, this metadata is associated with the entire capture action.
         DataIdentifier reference_id = 1;
     
    diff --git a/protos/bosdyn/api/data_buffer.proto b/protos/bosdyn/api/data_buffer.proto
    index 7948429bc..d79785e54 100644
    --- a/protos/bosdyn/api/data_buffer.proto
    +++ b/protos/bosdyn/api/data_buffer.proto
    @@ -271,10 +271,27 @@ message Event {
     
         // Optional set of event parameters.
         repeated bosdyn.api.Parameter parameters = 8;
    +
    +    // LogPreserveHint may encode a hint to the robot's logging system for whether to preserve
    +    // internal log data near the time of this event.  This could be useful in saving data
    +    // to be used in a service log to send to Boston Dynamics.
    +    enum LogPreserveHint {
    +        // If this this is unset, it is equivalent to LOG_PRESERVE_HINT_NORMAL.
    +        LOG_PRESERVE_HINT_UNSET = 0;
    +        // Do not change the robot's default log data preservation behavior in response to this
    +        // event.
    +        LOG_PRESERVE_HINT_NORMAL = 1;
    +        // Request that the robot try to preserve data near the time of this event.
    +        // Log space on the robot is limited, so this does not guarentee that the data will be
    +        // preserved.
    +        LOG_PRESERVE_HINT_PRESERVE = 2;
    +    }
    +
    +    // Optionally request that the robot try to preserve data near this time for a service log.
    +    LogPreserveHint log_preserve_hint = 9;
     }
     
     message RecordTextMessagesResponse {
    -    
         // Text message recording error.
         message Error {
             enum Type {
    @@ -300,7 +317,6 @@ message RecordTextMessagesResponse {
     }
     
     message RecordOperatorCommentsResponse {
    -
         // Operator comment recording error.
         message Error {
             enum Type {
    @@ -325,7 +341,6 @@ message RecordOperatorCommentsResponse {
     }
     
     message RecordDataBlobsResponse {
    -
         // DataBlob recording error.
         message Error {
             enum Type {
    @@ -351,7 +366,6 @@ message RecordDataBlobsResponse {
     }
     
     message RecordSignalTicksResponse {
    -
         // Signal tick recording error.
         message Error {
             enum Type {
    @@ -378,7 +392,6 @@ message RecordSignalTicksResponse {
     }
     
     message RecordEventsResponse {
    -
         // Event recording error.
         message Error {
             enum Type {
    diff --git a/protos/bosdyn/api/data_index.proto b/protos/bosdyn/api/data_index.proto
    index 6f463594b..9badd7b14 100644
    --- a/protos/bosdyn/api/data_index.proto
    +++ b/protos/bosdyn/api/data_index.proto
    @@ -30,9 +30,20 @@ message GrpcSpec {
     
     // Specification for selecting of blob messages.
     message BlobSpec {
    +    // If set, require the message source to match this.
         string source = 1;
    +
    +    // If set, require the message type to match this value.
         string message_type = 2;
    +
    +    // If both channel and channel_glob are set, messages are selected which match either one.
    +
    +    // If set, require the channel to match this value (or channel_glob, if set).
         string channel = 3;
    +
    +    // Optionally require the channel to match a glob (or channel, if set)..
    +    // For example, 'gps/*' will match all channels starting with 'gps/'.
    +    string channel_glob = 4;
     }
     
     
    @@ -41,6 +52,7 @@ message EventSpec {
         string source = 1;
         string type = 2;
         google.protobuf.Int32Value level = 3;
    +    Event.LogPreserveHint log_preserve_hint = 4;
     }
     
     
    diff --git a/protos/bosdyn/api/directory.proto b/protos/bosdyn/api/directory.proto
    index b8a71ea19..2ebdc9235 100644
    --- a/protos/bosdyn/api/directory.proto
    +++ b/protos/bosdyn/api/directory.proto
    @@ -66,7 +66,7 @@ message Endpoint {
         // The IP address of the computer hosting this endpoint.
         string host_ip = 1;
     
    -    // The port number on which the endpoint is provided.
    +    // The port number on which the endpoint is provided, between 0 and 65535.
         int32 port = 2;
     }
     
    diff --git a/protos/bosdyn/api/docking/docking.proto b/protos/bosdyn/api/docking/docking.proto
    index 07573dc17..ea8797b0b 100644
    --- a/protos/bosdyn/api/docking/docking.proto
    +++ b/protos/bosdyn/api/docking/docking.proto
    @@ -61,6 +61,17 @@ message DockingCommandResponse {
     
             // ERROR: Trying to undock while not docked
             STATUS_ERROR_NOT_DOCKED = 6;
    +
    +        // ERROR: Trying to dock when the arm is holding an object.
    +        STATUS_ERROR_GRIPPER_HOLDING_ITEM = 8;
    +
    +        // ERROR: The dock is not available for docking.
    +        STATUS_ERROR_NOT_AVAILABLE =  9;
    +
    +        // ERROR: Internal system error during execution
    +        // This error cannot be resolved by issuing a new DockingCommand
    +        // Check the returned message for details
    +        STATUS_ERROR_SYSTEM = 7;
         }
     
         // Common response header.
    @@ -123,6 +134,9 @@ message DockingCommandFeedbackResponse {
             // ERROR: Provided end time too far in the future.
             STATUS_ERROR_TOO_DISTANT = 8;
     
    +        // ERROR: The dock is not available for docking.
    +        STATUS_ERROR_NOT_AVAILABLE =   12;
    +
             // ERROR: Internal system error during execution
             // This error cannot be resolved by issuing a new DockingCommand
             // Check the returned message for details
    @@ -186,7 +200,8 @@ enum PrepPoseBehavior {
     // Message describing the overall dock state of the robot, including power & comms connections.  \
     // Not tied to any particular DockingCommand ID.  \
     // Note: [*] indicates fields which are only valid if the status is DOCK_STATUS_DOCKED or DOCK_STATUS_DOCKING  \
    -// Note: [^] indicates fields which are only valid if the status is DOCK_STATUS_DOCKED  \
    +// or DOCK_STATUS_UNDOCKING. \
    +// Note: [^] indicates fields which are only valid if the status is DOCK_STATUS_DOCKED.  \
     message DockState {
         enum DockedStatus {
             // Unknown
    @@ -197,10 +212,14 @@ message DockState {
             DOCK_STATUS_DOCKING = 2;
             // Robot is not detected as on dock
             DOCK_STATUS_UNDOCKED = 3;
    +        // Robot is currently running an undocking command
    +        DOCK_STATUS_UNDOCKING = 4;
         }
         enum LinkStatus {
             // Unknown or Not applicable
             LINK_STATUS_UNKNOWN = 0;
    +        // The link status is being detected
    +        LINK_STATUS_DETECTING = 3;
             // The link is detected as connected
             LINK_STATUS_CONNECTED = 1;
             // The link could not be detected
    diff --git a/protos/bosdyn/api/estop.proto b/protos/bosdyn/api/estop.proto
    index cbf5563f1..6deaecb99 100644
    --- a/protos/bosdyn/api/estop.proto
    +++ b/protos/bosdyn/api/estop.proto
    @@ -221,6 +221,9 @@ message DeregisterEstopEndpointResponse {
     
             // Registered to wrong configuration.
             STATUS_CONFIG_MISMATCH = 3;
    +
    +        // You cannot deregister an endpoint while the motors are on.
    +        STATUS_MOTORS_ON = 4;
         }
         // Status code for the response.
         Status status = 4;
    diff --git a/protos/bosdyn/api/full_body_command.proto b/protos/bosdyn/api/full_body_command.proto
    index 043c91b2b..6735546f2 100644
    --- a/protos/bosdyn/api/full_body_command.proto
    +++ b/protos/bosdyn/api/full_body_command.proto
    @@ -43,6 +43,8 @@ message FullBodyCommand {
                 // Command to perform payload mass property estimation
                 PayloadEstimationCommand.Request payload_estimation_request = 6;
     
    +            // Command to perform full body constrained manipulation moves
    +            ConstrainedManipulationCommand.Request constrained_manipulation_request = 7;
             }
     
             // Robot specific command parameters.
    @@ -75,6 +77,9 @@ message FullBodyCommand {
                 // Feedback for the payload estimation command request.
                 PayloadEstimationCommand.Feedback payload_estimation_feedback = 6;
     
    +            // Feedback for the constrained manipulation command request
    +            ConstrainedManipulationCommand.Feedback constrained_manipulation_feedback = 7;
    +
             }
     
             RobotCommandFeedbackStatus.Status status = 100;
    diff --git a/protos/bosdyn/api/geometry.proto b/protos/bosdyn/api/geometry.proto
    index 6d79aa2d2..431573f82 100644
    --- a/protos/bosdyn/api/geometry.proto
    +++ b/protos/bosdyn/api/geometry.proto
    @@ -8,10 +8,11 @@ syntax = "proto3";
     
     package bosdyn.api;
     
    -option java_outer_classname = "GeometryProto";
    -
     import "google/protobuf/wrappers.proto";
     
    +option go_package = "bosdyn/api";
    +option java_outer_classname = "GeometryProto";
    +
     // Two dimensional vector primitive.
     message Vec2 {
       double x = 1;
    @@ -49,6 +50,24 @@ message Plane {
       Vec3 normal = 2; // The direction of the planes normal.
     }
     
    +// A square oriented in 3D space.
    +message Quad {
    +    // The center of the quad and the orientation of the normal.
    +    // The normal axis is [0, 0, 1].
    +    SE3Pose pose = 1;
    +    // The side length of the quad.
    +    double size = 2;
    +}
    +
    +// A ray in 3D space.
    +message Ray {
    +    // Base of ray.
    +    Vec3 origin = 1;
    +
    +    // Unit vector defining the direction of the ray.
    +    Vec3 direction = 2;
    +}
    +
     // Geometric primitive to describe 2D position and rotation.
     message SE2Pose {
       Vec2 position = 1; // (m)
    diff --git a/protos/bosdyn/api/graph_nav/graph_nav.proto b/protos/bosdyn/api/graph_nav/graph_nav.proto
    index 654c3e2fa..15f021780 100644
    --- a/protos/bosdyn/api/graph_nav/graph_nav.proto
    +++ b/protos/bosdyn/api/graph_nav/graph_nav.proto
    @@ -10,6 +10,7 @@ package bosdyn.api.graph_nav;
     
     option java_outer_classname = "GraphNavProto";
     
    +import "bosdyn/api/basic_command.proto";
     import "bosdyn/api/data_chunk.proto";
     import "bosdyn/api/geometry.proto";
     import "bosdyn/api/graph_nav/nav.proto";
    @@ -140,6 +141,9 @@ message SetLocalizationResponse {
     
         // Alternative information if the localization is ambiguous.
         SuspectedAmbiguity suspected_ambiguity = 7;
    +
    +    // If the status is ROBOT_IMPAIRED, this is why the robot is impaired.
    +    RobotImpairedState impaired_state = 8;
     }
     
     message RouteGenParams {
    @@ -171,6 +175,13 @@ message TravelParams {
             TOLERANCE_IGNORE_POOR_FEATURE_QUALITY   = 2;    // Navigate through unlimited number of waypoints with poor quality features
         }
         FeatureQualityTolerance feature_quality_tolerance = 5;
    +
    +    // Disable directed exploration to skip blocked portions of route
    +    bool disable_directed_exploration = 6;
    +
    +
    +    // Disable alternate-route-finding; overrides the per-edge setting in the map.
    +    bool disable_alternate_route_finding = 8;
     }
     
     // The NavigateToRequest can be used to command GraphNav to drive the robot to a specific waypoint.
    @@ -264,6 +275,9 @@ message NavigateToResponse {
         // Return status for the request.
         Status status = 3;
     
    +    // If the status is ROBOT_IMPAIRED, this is why the robot is impaired.
    +    RobotImpairedState impaired_state = 6;
    +
         // Unique identifier for the command, If 0, command was not accepted.
         uint32 command_id = 4;
     
    @@ -271,6 +285,66 @@ message NavigateToResponse {
         repeated string error_waypoint_ids = 5;
     }
     
    +// These parameters are specific to how the robot follows a specified route in NavigateRoute.
    +message RouteFollowingParams {
    +    // For each enum in this message, if UNKNOWN is passed in, we default to one of the values
    +    // (indicated in the comments). Passing UNKNOWN is not considered a programming error.
    +
    +    // This setting applies when a new NavigateRoute command is issued (different route or
    +    // final-waypoint-offset), and command_id indicates a new command.
    +    enum StartRouteBehavior {
    +        // The mode is unset.
    +        START_UNKNOWN = 0;
    +        // The robot will find the shortest path to the start of the route, possibly using
    +        // edges that are not in the route. After going to the start, the robot will follow the
    +        // route.
    +        START_GOTO_START = 1;
    +        // The robot will find the shortest path to any point on the route, and go to the point
    +        // that gives that shortest path. Then, the robot will follow the rest of the route from
    +        // that point.
    +        // If multiple points on the route are similarly close to the robot, the robot will
    +        // prefer the earliest on the route.
    +        // This is the default.
    +        START_GOTO_ROUTE = 2;
    +        // The robot will fail the command with status STATUS_NOT_LOCALIZED_TO_ROUTE.
    +        START_FAIL_WHEN_NOT_ON_ROUTE = 3;
    +    }
    +
    +    // This setting applies when a NavigateRoute command is issued with the same route and
    +    // final-waypoint-offset. It is not necessary that command_id indicate the same command.
    +    // The expected waypoint is the last waypoint that GraphNav was autonomously navigating to.
    +    enum ResumeBehavior {
    +        // The mode is unset.
    +        RESUME_UNKNOWN = 0;
    +        // The robot will find the shortest path to any point on the route after the
    +        // furthest-along traversed edge, and go to the point that gives that shortest path.
    +        // Then, the robot will follow the rest of the route from that point.
    +        // This is the default.
    +        RESUME_RETURN_TO_UNFINISHED_ROUTE = 1;
    +        // The robot will fail the command with status STATUS_NOT_LOCALIZED_TO_ROUTE.
    +        RESUME_FAIL_WHEN_NOT_ON_ROUTE = 2;
    +    }
    +
    +    // This setting applies when the robot discovers that the route is blocked.
    +    enum RouteBlockedBehavior {
    +        // The mode is unset.
    +        ROUTE_BLOCKED_UNKNOWN = 0;
    +        // The robot will find the shortest path to any point after the furthest-along blockage,
    +        // and after the furthest-along traversed edge, and go to the point that gives that
    +        // shortest path. Then, the robot will follow the rest of the route from that point.
    +        // If multiple points on the route are similarly close to the robot, the robot will
    +        // prefer the earliest on the route.
    +        // This is the default.
    +        ROUTE_BLOCKED_REROUTE = 1;
    +        // The robot will fail the command with status STATUS_STUCK;
    +        ROUTE_BLOCKED_FAIL = 2;
    +    }
    +
    +    StartRouteBehavior new_cmd_behavior = 1;
    +    ResumeBehavior existing_cmd_behavior = 2;
    +    RouteBlockedBehavior route_blocked_behavior = 3;
    +}
    +
     // A NavigateRoute request message specifies a route of waypoints/edges and parameters
     // about how to get there. Like NavigateTo, this command returns immediately upon
     // processing and provides a command_id that the user can use along with a NavigationFeedbackRequest RPC to
    @@ -285,6 +359,10 @@ message NavigateRouteRequest {
         // A route for the robot to follow.
         Route route = 3;
     
    +    // What should the robot do if it is not at the expected point in the route, or the route is
    +    // blocked.
    +    RouteFollowingParams route_follow_params = 9;
    +
         // How to travel the route.
         TravelParams travel_params = 4;
     
    @@ -337,6 +415,8 @@ message NavigateRouteResponse {
             STATUS_UNKNOWN_ROUTE_ELEMENTS = 8;
             // [Route Error] One or more edges do not connect to expected waypoints.
             STATUS_INVALID_EDGE           = 9;
    +        // [Route Error] There is no path to the specified route.
    +        STATUS_NO_PATH                = 20;
             // [Route Error] Route contained a constraint fault.
             STATUS_CONSTRAINT_FAULT       = 11;
             // [Route Error] Route contained too many waypoints with low-quality features.
    @@ -346,6 +426,8 @@ message NavigateRouteResponse {
             // [Route Error] Happens when the current localization doesn't refer to any waypoint
             // in the route (possibly uninitialized localization).
             STATUS_NOT_LOCALIZED_TO_ROUTE = 16;
    +        // [Route Error] Happens when the current localization doesn't refer to any waypoint in the map (possibly uninitialized localization).
    +        STATUS_NOT_LOCALIZED_TO_MAP = 19;
     
             // [Wrestling Errors] Happens when graph nav refuses to follow the route you specified.  Try saying please?
             STATUS_COULD_NOT_UPDATE_ROUTE = 15;
    @@ -359,6 +441,9 @@ message NavigateRouteResponse {
         // Return status for the request.
         Status status = 3;
     
    +    // If the status is ROBOT_IMPAIRED, this is why the robot is impaired.
    +    RobotImpairedState impaired_state = 7;
    +
         // Unique identifier for the command, If 0, command was not accepted.
         uint32 command_id = 4;
     
    @@ -368,6 +453,118 @@ message NavigateRouteResponse {
         repeated Edge.Id error_edge_ids = 6;
     }
     
    +// The NavigateToAnchorRequest can be used to command GraphNav to drive the robot to a specific
    +// place in an anchoring. GraphNav will find the waypoint that has the shortest path length from
    +// robot's current position but is still close to the goal. GraphNav will plan a path through the
    +// map which most efficiently gets the robot to the goal waypoint, and will then travel
    +// in a straight line from the destination waypoint to the offset goal, attempting to avoid
    +// obstacles along the way.
    +// Parameters are provided which influence how GraphNav will generate and follow the path.
    +// This RPC returns immediately after the request is processed. It does not block until GraphNav
    +// completes the path to the goal waypoint. The user is expected to periodically check the status
    +// of the NavigateToAnchor command using the NavigationFeedbackRequest RPC.
    +message NavigateToAnchorRequest {
    +    // Common request header.
    +    RequestHeader header = 1;
    +
    +    // The Leases to show ownership of the robot and the graph.
    +    repeated Lease leases = 2;
    +
    +    // The goal, expressed with respect to the seed frame of the current anchoring.
    +    // The robot will use the z value to find the goal waypoint, but the final z height the robot
    +    // achieves will depend on the terrain height at the offset from the goal.
    +    SE3Pose seed_tform_goal = 3;
    +
    +    // These parameters control selection of the goal waypoint. In seed frame, they are the x, y,
    +    // and z tolerances with respect to the goal pose within which waypoints will be considered.
    +    // If these values are negative, or too small, reasonable defaults will be used.
    +    Vec3 goal_waypoint_rt_seed_ewrt_seed_tolerance = 4;
    +
    +    // Preferences on how to pick the route.
    +    RouteGenParams route_params = 6;
    +    // Parameters that define how to traverse and end the route.
    +    TravelParams travel_params = 7;
    +
    +    // The timestamp (in robot time) that the navigation command is valid until.
    +    google.protobuf.Timestamp end_time = 8;
    +
    +    // Identifier provided by the time sync service to verify time sync between robot and client.
    +    string clock_identifier = 9;
    +
    +    // Unique identifier for the command. If 0, this is a new command, otherwise it is a continuation
    +    // of an existing command. If this is a continuation of an existing command, all parameters will be
    +    // ignored, and the old parameters will be preserved.
    +    uint32 command_id = 10;
    +}
    +
    +// Response to a NavigateToAnchorRequest. This is returned immediately after the request is
    +// processed. A command_id is provided to specify the ID that the user may use to poll the system
    +// for feedback on the NavigateTo command.
    +message NavigateToAnchorResponse {
    +    // Common response header.
    +    ResponseHeader header = 1;
    +
    +    // Results of using the various leases.
    +    repeated LeaseUseResult lease_use_results = 2;
    +
    +    enum Status {
    +        // An unknown / unexpected error occurred.
    +        STATUS_UNKNOWN                = 0;
    +        // Request was accepted.
    +        STATUS_OK                     = 1;
    +
    +        // [Time error] Client has not done timesync with robot.
    +        STATUS_NO_TIMESYNC            = 2;
    +        // [Time error] The command was received after its end time had already passed.
    +        STATUS_EXPIRED                = 3;
    +        // [Time error]The command end time was too far in the future.
    +        STATUS_TOO_DISTANT            = 4;
    +
    +        // [Robot State Error] Cannot navigate a route if the robot has a critical
    +        //  perception fault, or behavior fault, or LIDAR not working.
    +        STATUS_ROBOT_IMPAIRED         = 5;
    +        // [Robot State Error] Cannot navigate a route while recording a map.
    +        STATUS_RECORDING              = 6;
    +
    +        // [Route Error] There is no anchoring.
    +        STATUS_NO_ANCHORING           = 7;
    +        // [Route Error] There is no path to a waypoint near the specified goal.
    +        //               If any waypoints were found (but no path), the error_waypoint_ids field
    +        //               will be filled.
    +        STATUS_NO_PATH                = 8;
    +
    +        // [Route Error] Route contained too many waypoints with low-quality features.
    +        STATUS_FEATURE_DESERT         = 10;
    +        // [Route Error] Happens when you try to issue a navigate to while the robot is lost.
    +        STATUS_LOST                   = 11;
    +        // [Route Error] Happens when the current localization doesn't refer to any waypoint in the map (possibly uninitialized localization).
    +        STATUS_NOT_LOCALIZED_TO_MAP = 13;
    +
    +        // [Wrestling error] Happens when graph nav refuses to follow the route you specified.
    +        STATUS_COULD_NOT_UPDATE_ROUTE = 12;
    +        // [Route Error] Happens when you try to issue a navigate to while the robot is stuck. Navigate to a different
    +        // waypoint, or clear the route and try again.
    +        STATUS_STUCK                  = 14;
    +        // [Request Error] Happens when you try to continue a command that was either expired, or had an unrecognized id.
    +        STATUS_UNRECOGNIZED_COMMAND   = 15;
    +
    +        // [Route Error] The pose is invalid, or known to be unachievable (upside-down, etc).
    +        STATUS_INVALID_POSE           = 16;
    +
    +    }
    +    // Return status for the request.
    +    Status status = 3;
    +
    +    // If the status is ROBOT_IMPAIRED, this is why the robot is impaired.
    +    RobotImpairedState impaired_state = 6;
    +
    +    // Unique identifier for the command, If 0, command was not accepted.
    +    uint32 command_id = 4;
    +
    +    // On a relevant error status code, these fields contain the waypoint/edge IDs that caused the error.
    +    repeated string error_waypoint_ids = 5;
    +}
    +
     // The NavigationFeedback request message uses the command_id of a navigation request to get
     // the robot's progress and current status for the command. Note that all commands return immediately
     // after they are processed, and the robot will continue to execute the command asynchronously until
    @@ -407,7 +604,7 @@ message NavigationFeedbackResponse {
             // The command expired.
             STATUS_COMMAND_TIMED_OUT = 7;
             // Cannot navigate a route if the robot has a crtical perception fault, or behavior fault,
    -        // or LIDAR not working.
    +        // or LIDAR not working. See impared_status for details.
             STATUS_ROBOT_IMPAIRED = 8;
             // The route constraints were not feasible.
             STATUS_CONSTRAINT_FAULT = 11;
    @@ -421,6 +618,9 @@ message NavigationFeedbackResponse {
         // Return status for the request.
         Status status = 2;
     
    +    // If the status is ROBOT_IMPAIRED, this is why the robot is impaired.
    +    RobotImpairedState impaired_state = 6;
    +
         // Remaining part of current route.
         Route remaining_route = 3;
     
    @@ -429,6 +629,11 @@ message NavigationFeedbackResponse {
     
         // The most recent transform describing the robot's pose relative to the navigation goal.
         SE3Pose last_ko_tform_goal = 5;
    +
    +    // Indicates whether the robot's body is currently in motion.
    +    SE2TrajectoryCommand.Feedback.BodyMovementStatus body_movement_status = 7;
    +
    +
     }
     
     // The GetLocalizationState request message requests the current localization state and any other
    @@ -538,6 +743,9 @@ message UploadGraphRequest {
         Graph graph = 2;
         // The Lease to show ownership of graph-nav service.
         Lease lease = 3;
    +
    +    // If this is true, generate an (overwrite the) anchoring on upload.
    +    bool generate_new_anchoring = 4;
     }
     
     // Response to the UploadGraphRequest. After uploading a graph, the user is expected
    @@ -552,6 +760,8 @@ message UploadGraphResponse {
             STATUS_OK = 1;
             // Can't upload the graph because it was too large for the license.
             STATUS_MAP_TOO_LARGE_LICENSE = 3;
    +        // The graph is invalid topologically, for example containing missing waypoints referenced by edges.
    +        STATUS_INVALID_GRAPH = 4;
         };
         // Status for an upload request.
         Status status = 8;
    @@ -654,6 +864,10 @@ message DownloadWaypointSnapshotRequest {
         // be used per point.
         bool compress_point_cloud = 4;
     
    +    // Skip downloading the point cloud, and only download other data such as images or world
    +    // objects.
    +    bool do_not_download_point_cloud = 5;
    +
     }
     
     // The DownloadWaypointSnapshot response streams the data of the waypoint snapshot id
    diff --git a/protos/bosdyn/api/graph_nav/graph_nav_service.proto b/protos/bosdyn/api/graph_nav/graph_nav_service.proto
    index 5c9ad1ca6..52286264f 100644
    --- a/protos/bosdyn/api/graph_nav/graph_nav_service.proto
    +++ b/protos/bosdyn/api/graph_nav/graph_nav_service.proto
    @@ -25,6 +25,9 @@ service GraphNavService {
         // Tell GraphNav to navigate to a waypoint along a route it chooses.
         rpc NavigateTo (NavigateToRequest) returns (NavigateToResponse) {}
     
    +    // Tell GraphNav to navigate to a goal with respect to the current anchoring.
    +    rpc NavigateToAnchor (NavigateToAnchorRequest) returns (NavigateToAnchorResponse) {}
    +
         // Get feedback on active navigation command.
         rpc NavigationFeedback (NavigationFeedbackRequest) returns (NavigationFeedbackResponse) {}
     
    diff --git a/protos/bosdyn/api/graph_nav/map.proto b/protos/bosdyn/api/graph_nav/map.proto
    index 6b6fd87bc..564191cb2 100644
    --- a/protos/bosdyn/api/graph_nav/map.proto
    +++ b/protos/bosdyn/api/graph_nav/map.proto
    @@ -15,6 +15,7 @@ import "bosdyn/api/local_grid.proto";
     import "bosdyn/api/point_cloud.proto";
     import "bosdyn/api/robot_state.proto";
     import "bosdyn/api/spot/robot_command.proto";
    +import "bosdyn/api/stairs.proto";
     import "bosdyn/api/world_object.proto";
     
     import "google/protobuf/field_mask.proto";
    @@ -135,6 +136,10 @@ message WaypointSnapshot {
         // If this snapshot is a modified version of the raw snapshot with the given ID (for example, it has been processed),
         // a new unique ID will we assigned to this field. If the field is empty, this is the raw version of the snapshot.
         string version_id = 9;
    +
    +    // If true, the point cloud contains data from a remote point cloud service,
    +    // such as LIDAR.
    +    bool has_remote_point_cloud_sensor = 10;
     }
     
     // A base element of the graph nav map. Edges consist of a directed edge from one
    @@ -171,52 +176,24 @@ message Edge {
             EDGE_SOURCE_FIDUCIAL_LOOP_CLOSURE = 3;
             // Edges that may help find alternate routes.
             EDGE_SOURCE_ALTERNATE_ROUTE_FINDING = 4;
    +        // Created via a CreateEdge RPC.
    +        EDGE_SOURCE_USER_REQUEST = 5;
         };
     
         // Annotations understood by BostonDynamics systems.
         message Annotations {
             // Velocity limits to use while traversing the edge.
             // These are maxima and minima, NOT target speeds.
    -        SE2VelocityLimit vel_limit = 1;
    +        // NOTE: as of 2.4 this is deprecated. Please use mobility_params.vel_limit.
    +        SE2VelocityLimit vel_limit = 1 [deprecated = true];
     
             // Defines any parameters of the stairs
             message StairData {
                 // Check this before reading other fields.
                 AnnotationState state = 1;
     
    -            message StraightStaircase {
    -                // The staircase origin is the bottom-center of the first rise.
    -                // It is expressed in ko frame of the from_waypoint.
    -                bosdyn.api.SE3Pose from_ko_tform_stairs = 1;
    -
    -                // A single stair from a staircase.
    -                message Stair {
    -                    // Height of each stair.
    -                    float rise = 1;
    -                    // Depth of each stair.
    -                    float run = 2;
    -                }
    -                // Straight staircases have two landings, one at the top and one at the bottom.
    -                // Landings are areas of free space before and after the stairs, and are represented
    -                // as oriented bounding boxes.
    -                message Landing {
    -                    // Pose of the landing's center relative to the stairs frame.
    -                    bosdyn.api.SE3Pose stairs_tform_landing_center = 1;
    -                    // The half-size of the box representing the landing in the x axis.
    -                    double landing_extent_x = 2;
    -                    // The half-size of the box representing the landing in the y axis.
    -                    double landing_extent_y = 3;
    -                }
    -                // Each stair should be rise followed by run. The last stair will have zero run.
    -                repeated Stair stairs = 2;
    -                // The lowermost landing of the stairs. The robot will try to
    -                // align itself to the stairs while on this landing.
    -                Landing bottom_landing = 3;
    -                // The uppermost landing of the stairs.
    -                Landing top_landing = 4;
    -            }
                 //  Parameters describing a straight staircase.
    -            StraightStaircase straight_staircase = 2;
    +            bosdyn.api.StraightStaircase straight_staircase = 2;
             }
             // Stairs information/parameters specific to the edge.
             StairData stairs = 2;
    @@ -259,7 +236,8 @@ message Edge {
             // stored in the map. For example, if this FieldMask contains "stair_hint" and
             // "terrain_params.enable_grated_floor", then the map will be
             // annotated with "stair_hint" and "enable_grated_floor" settings. An empty FieldMask means all fields are active
    -        // annotations.
    +        // annotations. Note that the more conservative of the velocity limit stored in the mobility parameters and the
    +        // TravelParams of the entire route will be used for this edge (regardless of what override_mobility_params says).
             google.protobuf.FieldMask override_mobility_params = 9;
             // Contains terrain parameters, swing height, obstacle avoidance parameters, etc.
             // When the robot crosses this edge, it will use the mobility parameters here.
    @@ -301,6 +279,33 @@ message EdgeSnapshot {
         repeated Stance stances = 2;
     }
     
    +// This associates a waypoint with a common reference frame, which is not necessarily metric.
    +message Anchor {
    +    // Identifier of the waypoint.
    +    string id = 1;
    +
    +    // Pose of the waypoint in the seed frame.
    +    SE3Pose seed_tform_waypoint = 2;
    +}
    +
    +// This associates a world object with a common reference frame, which is not necessarily metric.
    +message AnchoredWorldObject {
    +    // Identifier of the world object.
    +    string id = 1;
    +
    +    // Pose of the object in the seed frame.
    +    SE3Pose seed_tform_object = 2;
    +}
    +
    +message Anchoring {
    +    // The waypoint ids for the graph, expressed in a common reference frame, which is not
    +    // necessarily metric. If there is no anchoring, this is empty.
    +    repeated Anchor anchors = 1;
    +
    +    // World objects, located in the common reference frame.
    +    repeated AnchoredWorldObject objects = 2;
    +}
    +
     // This is an arbitrary collection of waypoints and edges. The edges and waypoints are not required
     // to be connected. A waypoint may belong to multiple graphs. This message is used to pass around
     // information about a graph's topology, and is used to serialize map topology to and from files.
    @@ -311,4 +316,7 @@ message Graph {
         repeated Waypoint waypoints = 1;
         // The edges connecting the graph's waypoints.
         repeated Edge edges = 2;
    +
    +    // The anchoring (mapping from waypoints to their pose in a shared reference frame).
    +    Anchoring anchoring = 3;
     }
    diff --git a/protos/bosdyn/api/graph_nav/map_processing.proto b/protos/bosdyn/api/graph_nav/map_processing.proto
    new file mode 100644
    index 000000000..24c4fb2db
    --- /dev/null
    +++ b/protos/bosdyn/api/graph_nav/map_processing.proto
    @@ -0,0 +1,362 @@
    +// Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +//
    +// Downloading, reproducing, distributing or otherwise using the SDK Software
    +// is subject to the terms and conditions of the Boston Dynamics Software
    +// Development Kit License (20191101-BDSDK-SL).
    +
    +syntax = "proto3";
    +
    +import "google/protobuf/wrappers.proto";
    +
    +import "bosdyn/api/geometry.proto";
    +import "bosdyn/api/header.proto";
    +import "bosdyn/api/graph_nav/map.proto";
    +
    +package bosdyn.api.graph_nav;
    +
    +// Processes a GraphNav map by creating additional edges. After processing,
    +// a new subgraph is created containing additional edges to add to the map.
    +// Edges are created between waypoints that are near each other. These waypoint pairs
    +// are called "loop closures", and are found by different means.
    +// In general, if parameters are not provided, reasonable defaults will be used.
    +// Note that this can be used to merge disconnected subgraphs from multiple recording
    +// sessions so long as they share fiducial observations.
    +message ProcessTopologyRequest {
    +    // Parameters for how to refine loop closure edges using iterative
    +    // closest point matching.
    +    message ICPParams {
    +        // The maximum number of iterations to run. Set to zero to skip ICP processing.
    +        google.protobuf.Int32Value icp_iters = 1;
    +        // The maximum distance between points in the point cloud we are willing to
    +        // accept for matches.
    +        google.protobuf.DoubleValue max_point_match_distance = 2;
    +    }
    +    // Parameters for how to close loops using odometry. This infers which waypoints
    +    // should be connected to one another based on the odometry measurements in the map.
    +    message OdometryLoopClosureParams {
    +        // The maximum distance between waypoints found by walking a path from one
    +        // waypoint to the other using only the existing edges in the map. Beyond
    +        // this distance, we are unwilling to trust odometry.
    +        google.protobuf.DoubleValue max_loop_closure_path_length = 1;
    +        // The minimum distance between waypoints found by walking a path from
    +        // one waypoint to the other using only the existing edges in the map.
    +        // Set this higher to avoid creating small shortcuts along the existing path.
    +        // Note that this is a 2d path length.
    +        google.protobuf.DoubleValue min_loop_closure_path_length = 2;
    +        // The maximum apparent height change of the created edge that we are
    +        // willing to accept between waypoints. This avoids closing loops up ramps,
    +        // stairs, etc. or closing loops where there is significant odometry drift.
    +        google.protobuf.DoubleValue max_loop_closure_height_change = 3;
    +        // Once a loop closure candidate is found, the system creates an edge between the
    +        // candidate waypoints. Only create the edge if it is shorter than this value.
    +        // Note that this is a 3d edge length.
    +        google.protobuf.DoubleValue max_loop_closure_edge_length = 4;
    +        // Use prior loop closures to infer new odometry based loop closures. This is
    +        // useful when other sources of loop closures (like fiducials) are being used.
    +        // The existence of those loop closures allows the system to infer other nearby
    +        // loop closures using odometry. Alternatively, the user may call the ProcessTopology
    +        // RPC multiple times to achieve the same effect.
    +        google.protobuf.Int32Value num_extra_loop_closure_iterations = 5;
    +    }
    +    // Parameters for how to close a loop using fiducials (AprilTags). This infers
    +    // which waypoints should be connected to one another based on shared observations
    +    // of AprilTags.
    +    // Note that multiple disconnected subgraphs (for example from multiple recording sessions)
    +    // can be merged this way.
    +    message FiducialLoopClosureParams {
    +        // The minimum distance between waypoints found by walking a path from
    +        // one waypoint to the other using only the existing edges in the map.
    +        // Set this higher to avoid creating small shortcuts along the existing path.
    +        // Note that this is a 2d path length.
    +        google.protobuf.DoubleValue min_loop_closure_path_length = 1;
    +        // Once a loop closure candidate is found, the system creates an edge between the
    +        // candidate waypoints. Only create the edge if it is shorter than this value.
    +        // Note that this is a 3d edge length.
    +        google.protobuf.DoubleValue max_loop_closure_edge_length = 2;
    +        // Maximum distance to accept between a waypoint and a fiducial detection to
    +        // use that fiducial detection for generating loop closure candidates.
    +        google.protobuf.DoubleValue max_fiducial_distance = 3;
    +        // The maximum apparent height change of the created edge that we are
    +        // willing to accept between waypoints. This avoids closing loops up ramps,
    +        // stairs, etc. or closing loops where there is significant odometry drift.
    +        google.protobuf.DoubleValue max_loop_closure_height_change = 4;
    +    }
    +    // Parameters for how to check for collisions when creating loop closures. The system
    +    // will avoid creating edges in the map that the robot cannot actually traverse due to
    +    // the presence of nearby obstacles.
    +    message CollisionCheckingParams {
    +        // By default, this is true.
    +        google.protobuf.BoolValue check_edges_for_collision = 1;
    +        // Assume that the robot is a sphere with this radius. Only accept a
    +        // loop closure if this spherical robot can travel in a straight line
    +        // from one waypoint to the other without hitting obstacles.
    +        google.protobuf.DoubleValue collision_check_robot_radius = 2;
    +        // Consider significant height variations along the edge (like stairs or ramps)
    +        // to be obstacles. The edge will not be created if there is a height change along
    +        // it of more than this value according to the nearby sensor data.
    +        google.protobuf.DoubleValue collision_check_height_variation = 3;
    +    }
    +    // Parameters which control topology processing. In general, anything which isn't filled out
    +    // will be replaced by reasonable defaults.
    +    message Params {
    +        // True by default -- generate loop closure candidates using odometry.
    +        google.protobuf.BoolValue do_odometry_loop_closure = 1;
    +        // Parameters for generating loop closure candidates using odometry.
    +        OdometryLoopClosureParams odometry_loop_closure_params = 2;
    +        // Parameters for refining loop closure candidates using iterative closest point
    +        // cloud matching.
    +        ICPParams icp_params = 3;
    +        // True by default -- generate loop closure candidates using fiducials.
    +        google.protobuf.BoolValue do_fiducial_loop_closure = 4;
    +        // Parameters for generating loop closure candidates using fiducials.
    +        FiducialLoopClosureParams fiducial_loop_closure_params = 5;
    +        // Parameters which control rejecting loop closure candidates which
    +        // collide with obstacles.
    +        CollisionCheckingParams collision_check_params = 6;
    +        // Causes the processing to time out after this many seconds. If not set, a default of 45 seconds
    +        // will be used. If this timeout occurs before the overall RPC timeout, a partial result will be
    +        // returned with ProcessTopologyResponse.timed_out set to true. Processing can be continued by
    +        // calling ProcessTopology again.
    +        double timeout_seconds = 7;
    +    }
    +    // Standard message header.
    +    RequestHeader header = 1;
    +    // Parameters. If not filled out, reasonable defaults will be used.
    +    Params params = 2;
    +
    +    // If true, any processing should directly modify the map on the server.
    +    // Otherwise, the client is expected to upload the processing results (newly created edges)
    +    // back to the server. The processing service shares memory with a map container service
    +    // (e.g the GraphNav service).
    +    bool modify_map_on_server = 3;
    +}
    +
    +// Result of the topology processing RPC. If successful, contains a subgraph of new
    +// waypoints or edges created by this process.
    +message ProcessTopologyResponse {
    +    // Standard message header.
    +    ResponseHeader header = 1;
    +
    +    enum Status {
    +        STATUS_UNKNOWN = 0; // Programming error.
    +        STATUS_OK = 1; // Success.
    +        STATUS_MISSING_WAYPOINT_SNAPSHOTS = 2; // Not all of the waypoint snapshots exist on the server. Upload them to continue.
    +        STATUS_INVALID_GRAPH = 3; // The graph is invalid topologically, for example containing missing waypoints referenced by edges.
    +        STATUS_MAP_MODIFIED_DURING_PROCESSING = 4; // Tried to write the anchoring after processing, but another client may have modified the map. Try again
    +    }
    +    // Result of the processing.
    +    Status status = 2;
    +
    +    // This graph contains the new edge(s) created by map processing. Note that these edges will be
    +    // annotated with their creation method. Note that several subgraphs may be returned via
    +    // streaming as the map is processed.
    +    Graph new_subgraph = 3;
    +
    +    // If modify_map_on_server was set to true in the request, then the map currently on the server
    +    // was modified using map processing. If this is set to false, then either an error occurred during
    +    // processing, or modify_map_on_server was set to false in the request.
    +    // When map_on_server_was_modified is set to false, the client is expected to upload the results
    +    // back to the server to commit the changes.
    +    bool map_on_server_was_modified = 4;
    +    // When there are missing waypoint snapshots, these are the IDs of the missing snapshots.
    +    // Upload them to continue.
    +    repeated string missing_snapshot_ids = 10;
    +    // When there are missing waypoints, these are the IDs of the missing waypoints. Upload them
    +    // to continue.
    +    repeated string missing_waypoint_ids = 11;
    +    // If true, the processing timed out. Note that this is not considered an error. Run topology processing again
    +    // to continue adding edges.
    +    bool timed_out = 12;
    +}
    +
    +// Represents an interval in x, y, z and yaw around some center. Some value x
    +// will be within the bounds if  center - x_bounds <= x >= center + x_bounds.
    +// If the values are left at zero, the bounds are considered to be unconstrained.
    +// The center of the bounds is left implicit, and should be whatever this message
    +// is packaged with.
    +message PoseBounds {
    +    // Bounds on the x position in meters.
    +    double x_bounds = 1;
    +    // Bounds on the y position in meters.
    +    double y_bounds = 2;
    +    // Bounds on the z position in meters.
    +    double z_bounds = 3;
    +    // Bounds on the yaw (rotation around z axis) in radians.
    +    double yaw_bounds= 4;
    +}
    +
    +// Controls how certain the user is of an anchor's pose. If left empty, a reasonable default will be chosen.
    +message AnchorHintUncertainty {
    +    oneof uncertainty {
    +        // A full 6x6 Gaussian covariance matrix representing uncertainty of an anchoring.
    +        SE3Covariance se3_covariance = 1;
    +        // Represents the 95 percent confidence interval on individual axes. This
    +        // will be converted to a SE3Covariance internally by creating a diagonal
    +        // matrix whose elements are informed by the confidence bounds.
    +        PoseBounds confidence_bounds = 2;
    +    }
    +}
    +
    +// Waypoints may be anchored to a particular seed frame. The user may request that a waypoint
    +// be anchored in a particular place with some Gaussian uncertainty.
    +message WaypointAnchorHint {
    +    // This is to be interpreted as the mean of a Gaussian distribution, representing
    +    // the pose of the waypoint in the seed frame.
    +    Anchor waypoint_anchor = 1;
    +    // This is the uncertainty of the anchor's pose in the seed frame.
    +    // If left empty, a reasonable default uncertainty will be generated.
    +    AnchorHintUncertainty seed_tform_waypoint_uncertainty = 2;
    +    // Normally, the optimizer will move the anchorings of waypoints based on context, to minimize the
    +    // overall cost of the optimization problem. By providing a constraint on pose, the user can ensure
    +    // that the anchors stay within a certain region in the seed frame.
    +    // Leaving this empty will allow the optimizer to move the anchoring from the hint as far as it likes.
    +    PoseBounds seed_tform_waypoint_constraint = 3;
    +}
    +
    +// World objects (such as fiducials) may be anchored to a particular seed frame. The user may request that an object
    +// be anchored in a particular place with some Gaussian uncertainty.
    +message WorldObjectAnchorHint {
    +    // This is to be interpreted as the mean of a Gaussian distribution, representing
    +    // the pose of the object in the seed frame.
    +    AnchoredWorldObject object_anchor = 1;
    +    // This is the uncertainty of the anchor's pose in the seed frame.
    +    // If left empty, a reasonable default uncertainty will be generated.
    +    AnchorHintUncertainty seed_tform_object_uncertainty = 2;
    +    // Normally, the optimizer will move the anchorings of object based on context, to minimize the
    +    // overall cost of the optimization problem. By providing a constraint on pose, the user can ensure
    +    // that the anchors stay within a certain region in the seed frame.
    +    // Leaving this empty will allow the optimizer to move the anchoring from the hint as far as it likes.
    +    PoseBounds seed_tform_object_constraint = 3;
    +}
    +
    +// The user may assign a number of world objects and waypoints a guess at where they are in the seed frame.
    +// These hints will be respected by the ProcessAnchoringRequest.
    +message AnchoringHint {
    +    // List of waypoints and hints as to where they are in the seed frame.
    +    repeated WaypointAnchorHint waypoint_anchors = 1;
    +    // List of world objects and hints as to where they are in the seed frame.
    +    repeated WorldObjectAnchorHint world_objects = 2;
    +}
    +
    +// Causes the server to optimize an existing anchoring, or generate a new anchoring for the map using the given parameters.
    +// In general, if parameters are not provided, reasonable defaults will be used.
    +// The new anchoring will be streamed back to the client, or modified on the server if desired.
    +message ProcessAnchoringRequest {
    +    // Parameters for procesing an anchoring.
    +    message Params {
    +        // Parameters affecting the underlying optimizer.
    +        message OptimizerParams {
    +            // Maximum iterations of the optimizer to run.
    +            google.protobuf.Int32Value max_iters = 1;
    +            // Maximum time the optimizer is allowed to run before giving up.
    +            google.protobuf.DoubleValue max_time_seconds = 2;
    +        }
    +        // Parameters which affect the measurements the optimzier uses to process the anchoring.
    +        message MeasurementParams {
    +            // If true, waypoints which share the same kinematic odometry
    +            // frame will be constrained to one another using it.
    +            google.protobuf.BoolValue use_kinematic_odometry = 1;
    +            // If true, waypoints which share the same visual odometry frame
    +            // will be constrained to one another using it.
    +            google.protobuf.BoolValue use_visual_odometry = 2;
    +            // If true, waypoints will be constrained so that the apparent pose of the
    +            // robot w.r.t the waypoint at the time of recording is consistent with gravity.
    +            google.protobuf.BoolValue use_gyroscope_measurements = 3;
    +            // If true, edges which were created by topology processing via loop closures will
    +            // be used as constraints.
    +            google.protobuf.BoolValue use_loop_closures = 4;
    +            // If true, world object measurements will be used to constrain waypoints to one another
    +            // when those waypoints co-observe the same world object.
    +            google.protobuf.BoolValue use_world_objects = 5;
    +        }
    +        // Relative weights to use for each of the optimizer's terms. These can be any positive value.
    +        // If set to zero, a reasonable default will be used. In general, the higher the weight, the more
    +        // the optimizer will care about that particular measurement.
    +        message Weights {
    +            double kinematic_odometry_weight = 1;
    +            double visual_odometry_weight = 2;
    +            double world_object_weight = 3;
    +            double hint_weight = 4;
    +            double gyroscope_weight = 5;
    +            double loop_closure_weight = 6;
    +        }
    +        OptimizerParams optimizer_params = 1;
    +        MeasurementParams measurement_params = 2;
    +        Weights weights = 3;
    +        // If true, the anchoring which already exists on the server will be used as the initial
    +        // guess for the optimizer. Otherwise, a new anchoring will be generated for every waypoint
    +        // which doesn't have a value passed in through initial_hint. If no hint is provided,
    +        // and this value is false, every waypoint will be given a starting anchoring based on
    +        // the oldest waypoint in the map.
    +        google.protobuf.BoolValue optimize_existing_anchoring = 4;
    +        // The optimizer will try to keep the orientation of waypoints consistent with gravity.
    +        // If provided, this is the gravity direction expressed with respect to the seed. This
    +        // will be interpreted as a unit vector. If not filled out, a default of (0, 0, -1) will be
    +        // used.
    +        bosdyn.api.Vec3 gravity_ewrt_seed = 5;
    +    }
    +    // Standard request header.
    +    RequestHeader header = 1;
    +    Params params = 2;
    +    // Initial guess at some number of waypoints and world objects and their anchorings.
    +    AnchoringHint initial_hint = 3;
    +    // If true, the map currently uploaded to the server will have its anchoring modified.
    +    // Otherwise, the user is expected to re-upload the anchoring.
    +    bool modify_anchoring_on_server = 4;
    +    // If true, the anchoring will be streamed back to the user after every iteration.
    +    // This is useful for debug visualization.
    +    bool stream_intermediate_results = 5;
    +}
    +
    +// Streamed response from the ProcessAnchoringRequest. These will be streamed until optimization is complete.
    +// New anchorings will be streamed as they become available.
    +message ProcessAnchoringResponse {
    +    ResponseHeader header = 1;
    +    enum Status {
    +        STATUS_UNKNOWN = 0; // Programming error.
    +        STATUS_OK = 1; // Success.
    +        STATUS_MISSING_WAYPOINT_SNAPSHOTS = 2; // Not all of the waypoint snapshots exist on the server. Upload them to continue.
    +        STATUS_INVALID_GRAPH = 3; // The graph is invalid topologically, for example containing missing waypoints referenced by edges.
    +        STATUS_OPTIMIZATION_FAILURE = 4; // The optimization failed due to local minima or an ill-conditioned problem definition.
    +        STATUS_INVALID_PARAMS = 5; // The parameters passed to the optimizer do not make sense (e.g negative weights).
    +        STATUS_CONSTRAINT_VIOLATION = 6; // One or more anchors were moved outside of the desired constraints.
    +        STATUS_MAX_ITERATIONS = 7; // The optimizer reached the maximum number of iterations before converging.
    +        STATUS_MAX_TIME = 8; // The optimizer timed out before converging.
    +        STATUS_INVALID_HINTS = 9; // One or more of the hints passed in to the optimizer are invalid (do not correspond to real waypoints or objects).
    +        STATUS_MAP_MODIFIED_DURING_PROCESSING = 10; // Tried to write the anchoring after processing, but another client may have modified the map. Try again.
    +    }
    +    Status status = 2;
    +    // Contains new anchorings for waypoint(s) processed by the server.
    +    // These will be streamed back to the user as they become available.
    +    repeated Anchor waypoint_results = 3;
    +    // Contains new anchorings for object(s) (e.g april tags) processed by the server.
    +    // These will be streamed back to the user as they become available
    +    repeated AnchoredWorldObject world_object_results = 4;
    +    // If modify_anchoring_on_server was set to true in the request, then the anchoring currently on the server
    +    // was modified using map processing. If this is set to false, then either an error occurred during
    +    // processing, or modify_anchoring_on_server was set to false in the request.
    +    // When anchoring_on_server_was_modified is set to false, the client is expected to upload the results
    +    // back to the server to commit the changes.
    +    bool anchoring_on_server_was_modified = 5;
    +    // The current optimizer iteration that produced these data.
    +    int32 iteration = 6;
    +    // The current nonlinear optimization cost.
    +    double cost = 7;
    +    // If true, this is the result of the final iteration of optimization.
    +    // This will always be true when stream_intermediate_results in the request is false.
    +    bool final_iteration = 8;
    +    // On failure due to constraint violation, these hints were violated by the optimization.
    +    // Try increasing the pose bounds on the constraints of these hints.
    +    repeated WaypointAnchorHint violated_waypoint_constraints = 9;
    +    // On failure due to constraint violation, these hints were violated by the optimization.
    +    // Try increasing the pose bounds on the constraints of these hints.
    +    repeated WorldObjectAnchorHint violated_object_constraints = 10;
    +    // When there are missing waypoint snapshots, these are the IDs of the missing snapshots.
    +    // Upload them to continue.
    +    repeated string missing_snapshot_ids = 11;
    +    // When there are missing waypoints, these are the IDs of the missing waypoints. Upload them
    +    // to continue.
    +    repeated string missing_waypoint_ids = 12;
    +    // Unorganized list of waypoints and object IDs which were invalid (missing from the map).
    +    repeated string invalid_hints = 13;
    +}
    \ No newline at end of file
    diff --git a/protos/bosdyn/api/graph_nav/map_processing_service.proto b/protos/bosdyn/api/graph_nav/map_processing_service.proto
    new file mode 100644
    index 000000000..b372da149
    --- /dev/null
    +++ b/protos/bosdyn/api/graph_nav/map_processing_service.proto
    @@ -0,0 +1,25 @@
    +// Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +//
    +// Downloading, reproducing, distributing or otherwise using the SDK Software
    +// is subject to the terms and conditions of the Boston Dynamics Software
    +// Development Kit License (20191101-BDSDK-SL).
    +
    +syntax = "proto3";
    +
    +package bosdyn.api.graph_nav;
    +
    +option java_outer_classname = "MapProcessingServiceProto";
    +
    +import "bosdyn/api/graph_nav/map_processing.proto";
    +
    +// Defines services for processing an existing GraphNav map.
    +service MapProcessingService {
    +
    +    // Processes a GraphNav map by creating additional edges or waypoints. After processing,
    +    // a new subgraph is created containing additional waypoints or edges to add to the map.
    +    rpc ProcessTopology(ProcessTopologyRequest) returns (stream ProcessTopologyResponse) {}
    +
    +    // Processes a GraphNav map by modifying the anchoring of waypoints and world objects in the map
    +    // with respect to a seed frame. After processing, a new anchoring is streamed back.
    +    rpc ProcessAnchoring(ProcessAnchoringRequest) returns (stream ProcessAnchoringResponse) {}
    +}
    diff --git a/protos/bosdyn/api/graph_nav/nav.proto b/protos/bosdyn/api/graph_nav/nav.proto
    index f6bbb3216..693f7339a 100644
    --- a/protos/bosdyn/api/graph_nav/nav.proto
    +++ b/protos/bosdyn/api/graph_nav/nav.proto
    @@ -33,7 +33,8 @@ message Localization {
         string waypoint_id = 1;
         // Pose of body in waypoint frame.
         SE3Pose waypoint_tform_body = 2;
    -    // Pose of body to starting fiducial frame (origin).
    +    // Pose of body in a common reference frame. The common reference frame defaults to the starting
    +    // fiducial frame, but can be changed. See Anchoring for more info.
         SE3Pose seed_tform_body = 5;
         // Time (in robot time basis) that this localization was valid.
         google.protobuf.Timestamp timestamp = 3;
    diff --git a/protos/bosdyn/api/graph_nav/recording.proto b/protos/bosdyn/api/graph_nav/recording.proto
    index aded62b56..9efaf0e98 100644
    --- a/protos/bosdyn/api/graph_nav/recording.proto
    +++ b/protos/bosdyn/api/graph_nav/recording.proto
    @@ -11,6 +11,7 @@ package bosdyn.api.graph_nav;
     import "bosdyn/api/header.proto";
     import "bosdyn/api/lease.proto";
     import "bosdyn/api/license.proto";
    +import "bosdyn/api/world_object.proto";
     import "bosdyn/api/graph_nav/map.proto";
     
     option java_outer_classname = "RecordingProto";
    @@ -102,6 +103,9 @@ message StartRecordingResponse {
             STATUS_REMOTE_CLOUD_FAILURE_NO_DATA = 8;
             // All fiducials are visible but at least one pose could not be determined accurately.
             STATUS_FIDUCIAL_POSE_NOT_OK = 9;
    +        // When recording branches, the robot is too far from the existing map when starting
    +        // to record a new branch.
    +        STATUS_TOO_FAR_FROM_EXISTING_MAP = 10;
         }
         // Return status for the request.
         Status status = 4;
    @@ -178,6 +182,8 @@ message CreateWaypointRequest {
         // are presently visible before creating a waypoint. This is useful for verifying
         // that the robot is where the user thinks it is in an area with known fiducials.
         repeated int32 require_fiducials = 5;
    +    // Additional world objects to insert into this waypoint. 
    +    repeated WorldObject world_objects = 6;
     }
     
     // The CreateWaypoint response message contains the complete waypoint, and the associated
    diff --git a/protos/bosdyn/api/header.proto b/protos/bosdyn/api/header.proto
    index 3cd962908..99421b111 100644
    --- a/protos/bosdyn/api/header.proto
    +++ b/protos/bosdyn/api/header.proto
    @@ -8,11 +8,12 @@ syntax = "proto3";
     
     package bosdyn.api;
     
    -option java_outer_classname = "HeaderProto";
    -
     import "google/protobuf/any.proto";
     import "google/protobuf/timestamp.proto";
     
    +option go_package = "bosdyn/api";
    +option java_outer_classname = "HeaderProto";
    +
     // Standard header attached to all GRPC requests to services.
     message RequestHeader {
         // Time that the request was sent, as measured by the client's local system clock.
    diff --git a/protos/bosdyn/api/image.proto b/protos/bosdyn/api/image.proto
    index f983e7c67..2784bf985 100644
    --- a/protos/bosdyn/api/image.proto
    +++ b/protos/bosdyn/api/image.proto
    @@ -62,7 +62,8 @@ message Image {
         // How the image is encoded.
         Format format = 5;
     
    -    // If Format does not imply PixelFormat, this will be set.
    +    // Pixel format of the image; this will be set even when the Format implies
    +    // the pixel format.
         PixelFormat pixel_format = 6;
     }
     
    diff --git a/protos/bosdyn/api/ir_enable_disable.proto b/protos/bosdyn/api/ir_enable_disable.proto
    new file mode 100644
    index 000000000..8c290c233
    --- /dev/null
    +++ b/protos/bosdyn/api/ir_enable_disable.proto
    @@ -0,0 +1,35 @@
    +// Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +//
    +// Downloading, reproducing, distributing or otherwise using the SDK Software
    +// is subject to the terms and conditions of the Boston Dynamics Software
    +// Development Kit License (20191101-BDSDK-SL).
    +
    +syntax = "proto3";
    +
    +package bosdyn.api;
    +option java_outer_classname = "IREnableDisableProto";
    +
    +import "bosdyn/api/header.proto";
    +
    +message IREnableDisableRequest {
    +    RequestHeader header = 1;  ///< Common request header.
    +    
    +    enum Request {
    +        // Unspecified value -- should not be used.
    +        REQUEST_UNKNOWN = 0;
    +
    +        // Disable emissions.
    +        REQUEST_OFF = 1;
    +
    +        // Enable emissions.
    +        REQUEST_ON = 2;
    +    }    
    +    Request request = 2;
    +}
    +
    +
    +message IREnableDisableResponse {
    +    ResponseHeader header = 1;  ///< Common response header.
    +}
    +
    +
    diff --git a/protos/bosdyn/api/ir_enable_disable_service.proto b/protos/bosdyn/api/ir_enable_disable_service.proto
    new file mode 100644
    index 000000000..5577d71d1
    --- /dev/null
    +++ b/protos/bosdyn/api/ir_enable_disable_service.proto
    @@ -0,0 +1,17 @@
    +// Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +//
    +// Downloading, reproducing, distributing or otherwise using the SDK Software
    +// is subject to the terms and conditions of the Boston Dynamics Software
    +// Development Kit License (20191101-BDSDK-SL).
    +
    +syntax = "proto3";
    +
    +package bosdyn.api;
    +
    +option java_outer_classname = "IREnableDisableServiceProto";
    +
    +import "bosdyn/api/ir_enable_disable.proto";
    +
    +service IREnableDisableService {
    +    rpc IREnableDisable(IREnableDisableRequest) returns (IREnableDisableResponse) {}
    +}
    diff --git a/protos/bosdyn/api/lease.proto b/protos/bosdyn/api/lease.proto
    index 127ace2e1..de7b5439c 100644
    --- a/protos/bosdyn/api/lease.proto
    +++ b/protos/bosdyn/api/lease.proto
    @@ -34,6 +34,17 @@ message Lease {
         repeated string client_names = 4;
     }
     
    +
    +// Lease resources can be divided into a hierarchy of sub-resources that can
    +// be commanded together. This message describes the hierarchy of a resource.
    +message ResourceTree {
    +    // The name of this resource.
    +    string resource = 1;
    +
    +    // Sub-resources that make up this resource.
    +    repeated ResourceTree sub_resources = 2;
    +}
    +
     // Details about who currently owns the Lease for a resource.
     message LeaseOwner {
         string client_name = 1; // The name of the client application.
    @@ -75,6 +86,12 @@ message LeaseUseResult {
     
         // The previous lease, if any, which was used.
         Lease previous_lease = 4;
    +
    +    // The "latest"/"most recent" lease known to the system.
    +    Lease latest_known_lease = 5;
    +
    +    // Represents the latest "leaf" resources of the hierarchy.
    +    repeated Lease latest_resources = 6;
     }
     
     // The AcquireLease request message which sends which resource the lease should be for.
    @@ -241,6 +258,11 @@ message ListLeasesResponse {
     
         // The resources managed by the LeaseService.
         repeated LeaseResource resources = 2;
    +
    +    // Provide the hierarchical lease structure.
    +    // A resource can encapsulate multiple sub-resources.
    +    // For example, the "body" lease may include control of the legs, arm, and gripper.
    +    ResourceTree resource_tree = 3;
     }
     
     // The RetainLease request will inform the LeaseService that the application contains to hold
    @@ -263,3 +285,5 @@ message RetainLeaseResponse {
         // Result of using the lease.
         LeaseUseResult lease_use_result = 2;
     }
    +
    +
    diff --git a/protos/bosdyn/api/lease_service.proto b/protos/bosdyn/api/lease_service.proto
    index 56bc660bf..67e8ffffa 100644
    --- a/protos/bosdyn/api/lease_service.proto
    +++ b/protos/bosdyn/api/lease_service.proto
    @@ -36,4 +36,5 @@ service LeaseService {
     
         // Retain possession of a lease.
         rpc RetainLease(RetainLeaseRequest) returns (RetainLeaseResponse);
    +
     }
    diff --git a/protos/bosdyn/api/manipulation_api.proto b/protos/bosdyn/api/manipulation_api.proto
    index 6d5dc6c93..769c9cd71 100644
    --- a/protos/bosdyn/api/manipulation_api.proto
    +++ b/protos/bosdyn/api/manipulation_api.proto
    @@ -14,6 +14,7 @@ import "bosdyn/api/geometry.proto";
     import "bosdyn/api/header.proto";
     import "bosdyn/api/image.proto";
     import "bosdyn/api/lease.proto";
    +import "bosdyn/api/robot_state.proto";
     import "google/protobuf/wrappers.proto";
     
     message WalkToObjectRayInWorld {
    @@ -66,7 +67,7 @@ message PickObjectRayInWorld {
         //  cast a ray from the camera out to 4 meters (well past the object). \
         //  To do this you'd set: \
         //      ray_start_rt_frame: camera's position \
    -    //      ray_end_rt_frame: camera's position + unit vector along ray of interest * 4 meters \
    +    //      ray_end_rt_frame: camera's position + unit vector along ray of interest * 4 meters
         Vec3 ray_start_rt_frame = 1;
         Vec3 ray_end_rt_frame = 2;
     
    @@ -133,6 +134,7 @@ message PickObjectInImage {
         reserved 5, 6, 7, 8;
     }
     
    +
     message GraspParams {
         // Where the grasp is on the hand.  Set to 0 to be a (default) palm grasp, where the object will
         // be pressed against the gripper's palm plate.  Set to 1.0 to be a fingertip grasp, where the
    @@ -218,11 +220,25 @@ message VectorAlignmentWithTolerance {
     message SqueezeGrasp {
         // A "squeeze grasp" is a top-down grasp where we try to keep both jaws of the gripper in
         // contact with the ground and bring the jaws together.  This can allow the robot to pick up
    -    // small objects on the ground. \
    -    // If you specify a SqueezeGrasp with no other allowable orientations, then the robot
    -    // will perform this grasp.  If you specify it will at least one other allowable orientation,
    -    // the robot will attempt to find a normal grasp with that orientation and if it fails, will
    -    // try a compliant grasp.
    +    // small objects on the ground.
    +    //
    +    // If you specify a SqueezeGrasp as:
    +    //      allowed:
    +    //          - with no other allowable orientations:
    +    //              then the robot will perform a squeeze grasp.
    +    //          - with at least one other allowable orientation:
    +    //              the robot will attempt to find a normal grasp with that orientation and if it
    +    //              fails, will perform a squeeze grasp.
    +    //      disallowed:
    +    //          - with no other allowable orientations:
    +    //              the robot will perform an unconstrained grasp search and a grasp if a good grasp
    +    //              is found.  If no grasp is found, the robot will report
    +    //              MANIP_STATE_GRASP_PLANNING_NO_SOLUTION
    +    //          - with other allowable orientations:
    +    //              the robot will attempt to find a valid grasp.  If it cannot it will report
    +    //              MANIP_STATE_GRASP_PLANNING_NO_SOLUTION
    +
    +    bool squeeze_grasp_disallowed = 1;
     }
     
     message ManipulationApiFeedbackRequest {
    @@ -263,6 +279,9 @@ message ManipulationApiResponse {
         // ID of the manipulation command either just issued or that we are providing feedback for.
         int32 manipulation_cmd_id = 5;
     
    +    // Details about how the lease was used.
    +    LeaseUseResult lease_use_result = 6;
    +
         reserved 2, 3, 4;
     }
     
    @@ -290,6 +309,10 @@ enum ManipulationFeedbackState {
         MANIP_STATE_GRASP_PLANNING_WAITING_DATA_AT_EDGE = 13;
         MANIP_STATE_WALKING_TO_OBJECT = 10;
         MANIP_STATE_ATTEMPTING_RAYCASTING = 12;
    +    MANIP_STATE_MOVING_TO_PLACE = 14;
    +    MANIP_STATE_PLACE_FAILED_TO_RAYCAST_INTO_MAP = 15;
    +    MANIP_STATE_PLACE_SUCCEEDED = 16;
    +    MANIP_STATE_PLACE_FAILED = 17;
     }
     
     enum ManipulationCameraSource {
    @@ -343,6 +366,7 @@ message ManipulationApiRequest {
     
             // Execute a previously planned pick.
             PickObjectExecutePlan pick_object_execute_plan = 14;
    +
         }
     
         reserved 3, 6, 9;
    @@ -363,11 +387,21 @@ message ApiGraspOverride {
         Override override_request = 1;
     }
     
    +// Use this message to assert properties about the grasped item.
    +// By default, the robot will assume all grasped items are not carriable.
    +message ApiGraspedCarryStateOverride {
    +    ManipulatorState.CarryState override_request = 1;
    +}
    +
     message ApiGraspOverrideRequest {
         // Common request header.
         RequestHeader header = 1;
     
         ApiGraspOverride api_grasp_override = 4;
    +
    +    // If the grasp override is set to NOT_HOLDING, setting a carry_state_override
    +    // message will cause the request to be rejected as malformed.
    +    ApiGraspedCarryStateOverride carry_state_override = 2;
     }
     
     message ApiGraspOverrideResponse {
    diff --git a/protos/bosdyn/api/mission/mission.proto b/protos/bosdyn/api/mission/mission.proto
    index 9f1535965..42ccd1f81 100644
    --- a/protos/bosdyn/api/mission/mission.proto
    +++ b/protos/bosdyn/api/mission/mission.proto
    @@ -15,7 +15,6 @@ import "bosdyn/api/header.proto";
     import "bosdyn/api/lease.proto";
     import "bosdyn/api/mission/nodes.proto";
     import "bosdyn/api/mission/util.proto";
    -
     import "google/protobuf/timestamp.proto";
     import "google/protobuf/wrappers.proto";
     
    @@ -98,6 +97,8 @@ message State {
             STATUS_ERROR = 5;
             // No mission has been loaded.
             STATUS_NONE = 6;
    +        // The mission was stopped before completion.
    +        STATUS_STOPPED = 7;
         }
         // Current status of the mission.
         Status status = 4;
    @@ -197,6 +198,8 @@ message FailedNode {
         string name = 1;
         // The reason why this node failed. May not be provided by all nodes.
         string error = 2;
    +    // The type of node, e.g. "bosdyn.api.mission.Sequence". May not be provided by all nodes.
    +    string impl_typename = 3;
     }
     
     // A request to play the currently loaded mission for a fixed amount of time.
    @@ -225,6 +228,12 @@ message PlayMissionRequest {
     message PlaySettings {
         // Velocity limits on the robot motion. Example use: limit velocity in "navigate to" nodes.
         SE2VelocityLimit velocity_limit = 1;
    +
    +    // Disable directed exploration to bypass blocked path sections
    +    bool disable_directed_exploration = 2;
    +
    +    // Disable alternate-route-finding; overrides the per-edge setting in the map.
    +    bool disable_alternate_route_finding = 3;
     }
     
     
    @@ -384,6 +393,37 @@ message PauseMissionResponse {
         bosdyn.api.LeaseUseResult lease_use_result = 3;
     }
     
    +// The StopMission request message will fully stop the mission.
    +message StopMissionRequest {
    +    // Common request header.
    +    RequestHeader header = 1;
    +    // Lease on the mission service.
    +    bosdyn.api.Lease lease = 2;
    +}
    +
    +// The StopMission response message will return the status of the request.
    +message StopMissionResponse {
    +    // Common response header.
    +    ResponseHeader header = 1;
    +    // Possible results of a stop request.
    +    enum Status {
    +        // Invalid status, do not use.
    +        STATUS_UNKNOWN = 0;
    +        // Mission is stopped/complete.
    +        // The mission state may be in any of the "complete states", e.g. if the mission completed
    +        // successfully before this RPC took effect, the mission will report STATUS_SUCCESS and not
    +        // STATUS_STOPPED.
    +        STATUS_OK = 1;
    +        // No mission has started playing.
    +        // NOT returned if the mission is already stopped. In that case, you will get STATUS_OK.
    +        STATUS_NO_MISSION_PLAYING = 2;
    +    }
    +    // Result of the stop request.
    +    Status status = 2;
    +    // Result of the lease in the stop request.
    +    bosdyn.api.LeaseUseResult lease_use_result = 3;
    +}
    +
     // For requesting the mission as it was loaded in LoadMission.
     message GetMissionRequest {
         // Common request header.
    diff --git a/protos/bosdyn/api/mission/mission_service.proto b/protos/bosdyn/api/mission/mission_service.proto
    index cf21f8c73..dd9598098 100644
    --- a/protos/bosdyn/api/mission/mission_service.proto
    +++ b/protos/bosdyn/api/mission/mission_service.proto
    @@ -10,6 +10,7 @@ package bosdyn.api.mission;
     
     option java_outer_classname = "MissionServiceProto";
     
    +import "bosdyn/api/data_chunk.proto";
     import "bosdyn/api/mission/mission.proto";
     
     // The MissionService can be used to specify high level autonomous behaviors for Spot using behavior trees.
    @@ -17,6 +18,11 @@ service MissionService {
         // Load a mission to run later.
         rpc LoadMission(LoadMissionRequest) returns (LoadMissionResponse) {}
     
    +    // Alternative loading method for large missions, that allows you to break the 
    +    // mission up into multiple streamed requests.  The data chunks should deserialize
    +    // into a LoadMissionRequest
    +    rpc LoadMissionAsChunks (stream DataChunk) returns (LoadMissionResponse) {}
    +
         // Start executing a loaded mission.
         // Will not restart a mission that has run to completion. Use RestartMission to do that.
         rpc PlayMission(PlayMissionRequest) returns (PlayMissionResponse) {}
    @@ -24,6 +30,10 @@ service MissionService {
         // Pause mission execution.
         rpc PauseMission(PauseMissionRequest) returns (PauseMissionResponse) {}
     
    +    // Stop a running mission.
    +    // Must use RestartMission, not PlayMission, to begin from the beginning.
    +    rpc StopMission(StopMissionRequest) returns (StopMissionResponse) {}
    +
         // Start executing a loaded mission from the beginning.
         // Does not need to be called after LoadMission.
         rpc RestartMission(RestartMissionRequest) returns (RestartMissionResponse) {}
    diff --git a/protos/bosdyn/api/mission/nodes.proto b/protos/bosdyn/api/mission/nodes.proto
    index 51509cfc4..a5a1a1fc3 100644
    --- a/protos/bosdyn/api/mission/nodes.proto
    +++ b/protos/bosdyn/api/mission/nodes.proto
    @@ -12,6 +12,7 @@ option java_outer_classname = "NodesProto";
     
     import "google/protobuf/any.proto";
     import "google/protobuf/duration.proto";
    +import "google/protobuf/wrappers.proto";
     
     import "bosdyn/api/docking/docking.proto";
     import "bosdyn/api/geometry.proto";
    @@ -21,7 +22,9 @@ import "bosdyn/api/spot_cam/ptz.proto";
     import "bosdyn/api/robot_command.proto";
     import "bosdyn/api/power.proto";
     import "bosdyn/api/data_acquisition.proto";
    +import "bosdyn/api/data_buffer.proto";
     import "bosdyn/api/graph_nav/graph_nav.proto";
    +import "bosdyn/api/graph_nav/nav.proto";
     import "bosdyn/api/mission/util.proto";
     
     
    @@ -108,12 +111,26 @@ message Retry {
     
     // Run this child for a maximum amount of mission execution time.
     // Will exit with child's status if the child finishes early,
    -// FAILURE if the child remains in RUNNING state for too long.
    +// FAILURE if the child remains in RUNNING state for too long 
    +// and no timeout_child is specified, or the status of the
    +// timeout_child.
     message ForDuration {
         // Maximum duration of mission execution time.
         google.protobuf.Duration duration = 1;
    +
         // Child to execute for the duration.
         Node child = 2;
    +
    +    // Optional blackboard variable name.  If specified, this node will define a blackboard
    +    // variable that its child has access to, and write the number of seconds remaining as
    +    // a double to the blackboard under this name.
    +    string time_remaining_name = 3;
    +
    +    // Optional node that will run if the child times out.  If not specified, this node
    +    // will return FAILURE when the child times out.  If specified, and the
    +    // child times out, this node will return the status of the timeout_child.
    +    // The timeout_child does not respect the original timeout.
    +    Node timeout_child = 4;
     }
     
     
    @@ -241,6 +258,39 @@ message BosdynNavigateTo {
         bosdyn.api.graph_nav.RouteGenParams route_gen_params = 4;
         // Parameters that define how to traverse and end the route.
         bosdyn.api.graph_nav.TravelParams travel_params = 5;
    +
    +    // If provided, this will write the last NavigationFeedbackResponse message
    +    // to a blackboard variable with this name.
    +    string navigation_feedback_response_blackboard_key = 6;
    +    // If provided, this will write the last NavigateToResponse message to
    +    // a blackboard variable with this name.
    +    string navigate_to_response_blackboard_key = 7;
    +}
    +
    +
    +// Tell the robot to navigate a route.
    +message BosdynNavigateRoute {
    +    // Name of the service to use.
    +    string service_name = 1;
    +    // Host machine the service is running on.
    +    string host = 2;
    +
    +    // A route for the robot to follow.
    +    bosdyn.api.graph_nav.Route route = 3;
    +
    +    // What should the robot do if it is not at the expected point in the route, or the route is
    +    // blocked.
    +    bosdyn.api.graph_nav.RouteFollowingParams route_follow_params = 4;
    +
    +    // Parameters that define how to traverse and end the route.
    +    bosdyn.api.graph_nav.TravelParams travel_params = 5;
    +
    +    // If provided, this will write the last NavigationFeedbackResponse message
    +    // to a blackboard variable with this name.
    +    string navigation_feedback_response_blackboard_key = 6;
    +    // If provided, this will write the last NavigateRouteResponse message to
    +    // a blackboard variable with this name.
    +    string navigate_route_response_blackboard_key = 7;
     }
     
     // Get GraphNav state from the robot and save it to the blackboard.
    @@ -276,6 +326,18 @@ message BosdynGraphNavLocalize {
         bosdyn.api.graph_nav.SetLocalizationRequest localization_request = 3;
     }
     
    +// Record an APIEvent
    +message BosdynRecordEvent {
    +    // Name of the service to use.
    +    string service_name = 1;
    +    // Host machine the service is running on.
    +    string host = 2;
    +    // The event to be logged. Note that everything should be populated except the id, start_time
    +    // and end_time. The start and end time will be populated by the mission, using the node's start time.
    +    // The id field shouldn't be set when the start and end times are the same.
    +    bosdyn.api.Event event = 3;
    +}
    +
     // Call out to another system using the RemoteMission service.
     message RemoteGrpc {
         // Host that is running the directory server. Usually, this is just the robot.
    @@ -317,7 +379,9 @@ message Prompt {
         // door for the robot.
         bool always_reprompt = 1;
     
    -    // The text of the question itself.
    +    // The text of the question itself.  The question text may contain formatted blackboard
    +    // variables.  Please see the documentation in FormatBlackboard for more information
    +    // about supported string formats.
         string text = 2;
     
         // Metadata describing the source of the question.
    @@ -390,6 +454,28 @@ message SpotCamStoreMedia {
         string tag = 5;
     }
     
    +// Set the LEDs to a specified brightness
    +message SpotCamLed {
    +    // Name of the service to use.
    +    string service_name = 1;
    +
    +    // Host machine of the directory server that the Spot CAM registered with.
    +    string host = 2;
    +
    +    // Brightnesses of the LEDs, from SetLEDBrightnessRequest
    +    map brightnesses = 3;
    +}
    +
    +// Reset the autofocus on the Spot CAM PTZ
    +message SpotCamResetAutofocus {
    +    // Name of the service to use.
    +    string service_name = 1;
    +
    +    // Host machine of the directory server that the Spot CAM registered with.
    +    string host = 2;
    +}
    +
    +
     message Dock {
         // Name of the service to use.
         string service_name = 1;
    @@ -401,23 +487,60 @@ message Dock {
     
         // Optional child node. Children will have access to the status variables gathered by this node.
         // If specified, child node will determine success/failure of this node.
    -    Node child = 4;
    +    //
    +    // DEPRECATED!  Use docking_command_response_blackboard_key and
    +    // docking_command_feedback_response_blackboard_key instead.
    +    Node child = 4 [deprecated = true];
     
         // Name of the command status variable in the blackboard.  This is the status of the docking
         // command request made to the robot.  Please refer to
         // bosdyn.api.docking.DockingCommandResponse.Status for more details.  Children can use this
         // name to look up docking command status in the blackboard. If no name is provided, status will
         // not be available.
    -    string command_status_name = 5;
    +    //
    +    // DEPRECATED!  Use docking_command_response_blackboard_key and
    +    // docking_command_feedback_response_blackboard_key instead.
    +    string command_status_name = 5 [deprecated = true];
     
         // Name of the feedback status variable in the blackboard.  This is the feedback provided while
         // docking is in progress.  Please refer to bosdyn.api.docking.DockingCommandFeedbackResponse.Status
         // for a list of possible status values.  Children can use this name to look up docking status
         // in the blackboard. If no name is provided, status will not be available.
    -    string feedback_status_name = 6;
    +    //
    +    // DEPRECATED!  Use docking_command_response_blackboard_key and
    +    // docking_command_feedback_response_blackboard_key instead.
    +    string feedback_status_name = 6 [deprecated = true];
     
         // Defines how we use the "pre-docking" behavior.
         docking.PrepPoseBehavior prep_pose_behavior = 7;
    +
    +    // If provided, this will write the last DockingCommandFeedbackResponse message
    +    // to a blackboard variable with this name.
    +    string docking_command_feedback_response_blackboard_key = 8;
    +
    +    // If provided, this will write the last DockingCommandResponse message to
    +    // a blackboard variable with this name.
    +    string docking_command_response_blackboard_key = 9;
    +}
    +
    +// Triggers a StoreMetadataRequest to the data acquisition store.
    +message StoreMetadata {
    +    // Name of the service to use.
    +    string service_name = 1;
    +   
    +    // Host machine of the directory server that the data acquisition service is registered with.
    +    string host = 2;
    +
    +    // The name of the blackboard variable that holds the associated AcquireDataRequest. The
    +    // reference ID that this metadata is associated with will be copied from the request.
    +    string acquire_data_request_name = 3;
    +
    +    // The name of the metadata object in the blackboard to be stored.
    +    // The metadata object can be any protobuf message.
    +    string metadata_name = 5;
    +
    +    // The data buffer channel on which to store the metadata.
    +    string metadata_channel = 6;
     }
     
     // Trigger the acquisition and storage of data.
    @@ -447,6 +570,20 @@ message DataAcquisition{
         // Example: "{date}_loop_{loop_counter}", where "loop_counter" is a blackboard variable from a Repeat.
         string group_name_format = 5;
     
    +    // If populated, name of the variable in the blackboard in which to store the AcquireDataRequest.
    +    string request_name_in_blackboard = 6;
    +
    +}
    +
    +// Send RetainLease for every Lease the mission service is given via PlayMissionRequest.
    +// Returns RUNNING while there are more leases to retain, SUCCESS once a lease for each resource has
    +// been retained, and FAILURE if any one lease cannot be retained.
    +message RetainLease {
    +    // Name of the service to use.
    +    string service_name = 1;
    +
    +    // Host machine of the directory server that the lease service is registered with.
    +    string host = 2;
     }
     
     // Defines new blackboard variables within the scope of the child. Shadows blackboard
    @@ -478,9 +615,37 @@ message FormatBlackboard {
         string key = 1;
     
         // Define a format string that will be used together with the blackboard to generate
    -    // string value.  Values from the blackboard will replace the keys in braces {}.
    -    // Example: "telop-{date}", where "date" is a blackboard variable.
    -    // Example: "{date}_loop_{loop_counter}", where "loop_counter" is a blackboard variable from a Repeat.
    +    // string value.  Values from the blackboard will replace the keys in braces {}, i.e.
    +    // {blackboard_variable_name}.  We also allow some string formatting options, namely:
    +    //
    +    // 1) Floating point decimal places: {float_variable:.2f}
    +    // 2) TBD
    +    //
    +    // Select examples:
    +    //
    +    // Format String: "telop-{date}"
    +    //    Blackboard: "date" is a blackboard variable with string value: "2021-05-13"
    +    //        Output: "teleop-2021-05-13"
    +    //
    +    // Format String: "{date}_loop_{loop_counter}"
    +    //    Blackboard: "date" is a blackboard variable with string value: "2021-05-13"
    +    //    Blackboard: "loop_counter" is a blackboard variable with integer value: "3"
    +    //        Output: "2021-05-13_loop_3"
    +    //
    +    // Format String: "battery charge is: {state.power_state.locomotion_charge_percentage.value}"
    +    //    Blackboard: "state" is a protobuf message in the blackboard from a BosdynRobotState, and
    +    //                the power_state submessage has a charge percentage of 30.2148320923085
    +    //        Output: "battery charge is: 30.2158320923085"
    +    //
    +    // Format String: "battery charge is: {state.power_state.locomotion_charge_percentage.value:.2f}"
    +    //    Blackboard: "state" is a protobuf message in the blackboard from a BosdynRobotState, and
    +    //                the power_state submessage has a charge percentage of 30.2148320923085
    +    //        Output: "battery charge is: 30.21"
    +    //
    +    // Format String: "the value is {x:.0f}"
    +    //    Blackboard: "x" is a blackboard variable with float value: "2.71828"
    +    //        Output: "the value is 3"
    +    //
         string format = 2;
     }
     
    @@ -496,3 +661,10 @@ message ConstantResult {
         // This result is always returned when calling tick().
         Result result = 1;
     }
    +
    +// This node will run and return the status of the child node.
    +// If the mission is paused while this node is executing, the child will be
    +// restarted when the mission is resumed.
    +message RestartWhenPaused {
    +    Node child = 1;
    +}
    diff --git a/protos/bosdyn/api/network_compute_bridge.proto b/protos/bosdyn/api/network_compute_bridge.proto
    index e3b631fa2..a11df955c 100644
    --- a/protos/bosdyn/api/network_compute_bridge.proto
    +++ b/protos/bosdyn/api/network_compute_bridge.proto
    @@ -13,7 +13,6 @@ import "bosdyn/api/header.proto";
     import "bosdyn/api/image.proto";
     import "bosdyn/api/world_object.proto";
     import "google/protobuf/any.proto";
    -import "google/protobuf/duration.proto";
     
     message ListAvailableModelsRequest {
         RequestHeader header = 1; // Common request header
    @@ -28,10 +27,21 @@ message ListAvailableModelsResponse {
         // Provide list of available models
         repeated string available_models = 2;
     
    +    // Optional information about available classes for each model
    +    repeated ModelLabels labels = 6;
    +
         // Command status
         ListAvailableModelsStatus status = 5;
     }
     
    +message ModelLabels {
    +    // Model name.
    +    string model_name = 1;
    +
    +    // List of class labels returned by this model.
    +    repeated string available_labels = 2;
    +}
    +
     message NetworkComputeRequest {
         RequestHeader header = 1;  // Common request header.
     
    @@ -59,10 +69,10 @@ message NetworkComputeInputData {
             // Image to process, if you are not using an image source.
             Image image = 2;
     
    -        // Other data that isn't an image.  NetworkComputeBridge service will pass it through
    -        // to the remote server so you can do computation on arbitrary data.
    -        google.protobuf.Any other_data = 3;
         }
    +    // Other data that isn't an image.  NetworkComputeBridge service will pass it through
    +    // to the remote server so you can do computation on arbitrary data.
    +    google.protobuf.Any other_data = 3;
     
         // Name of the model to be run on the input data.
         string model_name = 4;
    @@ -115,6 +125,13 @@ message NetworkComputeResponse {
         // this field is not populated.  This field is not set for non-image input.
         ImageResponse image_response = 3;
     
    +    // If the image was rotated for processing, this field will contain the amount it was rotated by
    +    // (counter-clockwise, in radians).
    +    //
    +    // Note that the image returned is *not* rotated, regardless of if it was rotated
    +    // for processing.  This ensures that all other calibration and metadata remains valid.
    +    double image_rotation_angle = 6;
    +
         // Non image-type data that can optionally be returned by a remote server.
         google.protobuf.Any other_data = 4;
     
    diff --git a/protos/bosdyn/api/network_stats.proto b/protos/bosdyn/api/network_stats.proto
    new file mode 100644
    index 000000000..4ea608f03
    --- /dev/null
    +++ b/protos/bosdyn/api/network_stats.proto
    @@ -0,0 +1,82 @@
    +// Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +//
    +// Downloading, reproducing, distributing or otherwise using the SDK Software
    +// is subject to the terms and conditions of the Boston Dynamics Software
    +// Development Kit License (20191101-BDSDK-SL).
    +
    +syntax = "proto3";
    +
    +package bosdyn.api;
    +
    +import "google/protobuf/duration.proto";
    +
    +option java_outer_classname = "NetworkStatsProto";
    +
    +message Association {
    +    // MAC address of the associated station
    +    string mac_address = 1;
    +
    +    // Time duration since the station last connected.
    +    google.protobuf.Duration connected_time = 2;
    +
    +    // Signal strength of last received packet
    +    int32 rx_signal_dbm = 3;
    +
    +    // Signal strength average
    +    int32 rx_signal_avg_dbm = 4;
    +
    +    // Signal strength average for beacons only.
    +    int32 rx_beacon_signal_avg_dbm = 5;
    +
    +    // Expected throughput
    +    int64 expected_bits_per_second = 6;
    +
    +    // Total received bytes
    +    int64 rx_bytes = 7;
    +
    +    // Total received packets from the associated station
    +    int64 rx_packets = 8;
    +
    +    // Last unicast receive rate
    +    int64 rx_bits_per_second = 9;
    +
    +    // Total transmitted bytes
    +    int64 tx_bytes = 10;
    +
    +    // Total transmitted packets to the associated station
    +    int64 tx_packets = 11;
    +
    +    // Current unicast transmit rate
    +    int64 tx_bits_per_second = 12;
    +
    +    // Cumulative retry count to this station, within connected time
    +    int64 tx_retries = 13;
    +
    +    // Cumulative failed tx packet count to this station, within connected time
    +    int64 tx_failed = 14;
    +
    +    // Number of beacons received from this peer
    +    int64 beacons_received = 15;
    +
    +    // Number of times beacon loss was detected
    +    int64 beacon_loss_count = 16;
    +}
    +
    +message WifiDevice {
    +    enum Type {
    +        UNKNOWN = 0;
    +        AP = 1;
    +        CLIENT = 2;
    +    }
    +    Type type = 1;
    +    string name = 2;
    +    string mac_address = 3;
    +    string ssid = 4;
    +    int32 tx_power_dbm = 5;
    +    repeated Association associations = 6;
    +}
    +
    +message WifiStats {
    +    string hostname = 1;
    +    repeated WifiDevice devices = 2;
    +}
    diff --git a/protos/bosdyn/api/parameter.proto b/protos/bosdyn/api/parameter.proto
    index a64a9b0e9..026021f9e 100644
    --- a/protos/bosdyn/api/parameter.proto
    +++ b/protos/bosdyn/api/parameter.proto
    @@ -7,10 +7,12 @@
     syntax = "proto3";
     
     package bosdyn.api;
    -option java_outer_classname = "ParameterProto";
     
    -import "google/protobuf/timestamp.proto";
     import "google/protobuf/duration.proto";
    +import "google/protobuf/timestamp.proto";
    +
    +option go_package = "bosdyn/api";
    +option java_outer_classname = "ParameterProto";
     
     // A generic parameter message used by the robot state service to describe different,
     // parameterized aspects of the robot.
    @@ -39,6 +41,9 @@ message Parameter {
     
             // Value as true/false.
             bool bool_value = 8;
    +
    +        // Unsigned integer
    +        uint64 uint_value = 10;
         }
     
         // Description of the parameter or its value.
    diff --git a/protos/bosdyn/api/payload.proto b/protos/bosdyn/api/payload.proto
    index 26e7ce13d..5916f62b7 100644
    --- a/protos/bosdyn/api/payload.proto
    +++ b/protos/bosdyn/api/payload.proto
    @@ -42,6 +42,8 @@ message Payload {
         SE3Pose body_tform_payload = 7;
         // The pose of the payload relative to the mount frame.
         SE3Pose mount_tform_payload = 8;
    +    // Optional - mount frame_name (if not included, payload is assumed to be in the body mount frame)
    +    MountFrameName mount_frame_name = 13;
         // The mass and volume properties of the payload.
         PayloadMassVolumeProperties mass_volume_properties = 10;
         // A list of possible physical configurations for the payload.
    @@ -58,6 +60,8 @@ message PayloadPreset {
         string description = 2;
         // The pose of the payload relative to the body frame.
         SE3Pose mount_tform_payload = 3;
    +    // Optional - mount frame_name (if not included, payload is assumed to be in the body mount frame)
    +    MountFrameName mount_frame_name = 6;
         // The mass and volume properties of the payload.
         PayloadMassVolumeProperties mass_volume_properties = 4;
         // A list of labels used to indicate what type of payload this is.
    @@ -125,3 +129,15 @@ message ListPayloadsResponse {
         // The returned list of payloads registered in the directory.
         repeated Payload payloads = 2;
     }
    +
    +// Payloads are defined relative to a frame on the robot. These are the possible frames. 
    +enum MountFrameName {
    +    // The is the default. For backwards compatibility, we assume unknown means body mount frame. 
    +    MOUNT_FRAME_UNKNOWN = 0;
    +    // The body payload mount frame, as defined in documentation. 
    +    MOUNT_FRAME_BODY_PAYLOAD = 1;
    +    // The gripper payload mount frame, as defined in documentation. 
    +    MOUNT_FRAME_GRIPPER_PAYLOAD = 2;
    +    // The wrist link frame, as defined in the gripper CAD and documentation. 
    +    MOUNT_FRAME_WR1 = 3;
    +}
    diff --git a/protos/bosdyn/api/payload_registration.proto b/protos/bosdyn/api/payload_registration.proto
    index b727a1d55..e5906823a 100644
    --- a/protos/bosdyn/api/payload_registration.proto
    +++ b/protos/bosdyn/api/payload_registration.proto
    @@ -50,16 +50,19 @@ message RegisterPayloadResponse {
     
     // Update the payload definition of the payload with matching GUID. The existing payload must
     // have a secret set and the request must provide the secret for access.
    -// GUID, is_authorized, and is_enabled fields are immutable and cannot be updated.
    +// GUID and is_authorized fields are immutable and cannot be updated.
     message UpdatePayloadVersionRequest {
         // Common request header.
         RequestHeader header = 1;
     
    +    // Payload credentials.
    +    PayloadCredentials payload_credentials = 5;
    +
         // The GUID of the payload to be updated.
    -    string payload_guid = 2;
    +    string payload_guid = 2 [deprecated = true];
     
         // The payload secret for the specified payload.
    -    string payload_secret = 3;
    +    string payload_secret = 3 [deprecated = true];
     
         // The new software version that the payload is being updated to.
         SoftwareVersion updated_version = 4;
    @@ -94,11 +97,14 @@ message GetPayloadAuthTokenRequest {
         // Common request header.
         RequestHeader header = 1;
     
    +    // Payload credentials.
    +    PayloadCredentials payload_credentials = 4;
    +
         // The GUID to identify which payload to get the auth token for.
    -    string payload_guid = 2;
    +    string payload_guid = 2 [deprecated = true];
     
         // The payload secret for the specified payload.
    -    string payload_secret = 3;
    +    string payload_secret = 3 [deprecated = true];
     }
     
     // The GetPayloadAuthToken response message that returns the token for the payload.
    @@ -126,3 +132,57 @@ message GetPayloadAuthTokenResponse {
         // A limited-access user token provided on successful payload registration
         string token = 3;
     }
    +
    +// Attach/detach the payload with the matching GUID. The existing payload must
    +// have a secret set and the request must provide the secret for access.
    +// GUID is immutable and cannot be updated.
    +message UpdatePayloadAttachedRequest {
    +    // Common request header.
    +    RequestHeader header = 1;
    +
    +    // Payload credentials, used to identify the payload and authorize the changes. 
    +    PayloadCredentials payload_credentials = 2;
    +
    +    enum Request {
    +        REQUEST_UNKNOWN = 0;
    +        REQUEST_ATTACH = 1;
    +        REQUEST_DETACH = 2;
    +    }
    +    // Attach or detach the payload.
    +    Request request = 3;
    +}
    +
    +// The UpdatePayloadAttached response message contains the status of whether the update was successful.
    +message UpdatePayloadAttachedResponse {
    +    // Common response header.
    +    ResponseHeader header = 1;
    +
    +    enum Status {
    +        // UNKNOWN should never be used. An internal PayloadRegistrationService issue has happened if UNKNOWN is set.
    +        STATUS_UNKNOWN = 0;
    +
    +        // Success.  The payload version has been updated.
    +        STATUS_OK = 1;
    +
    +        // UpdatePayloadAttached failed because a payload with this GUID does not yet exist.
    +        STATUS_DOES_NOT_EXIST = 2;
    +
    +        // UpdatePayloadAttached failed because the paylod guid & secret do not match any registered payloads.
    +        STATUS_INVALID_CREDENTIALS = 3;
    +
    +        // UpdatePayloadAttached failed because the requested payload has not yet been authorized.
    +        // Authorize the payload in the webserver first, then try again.
    +        STATUS_PAYLOAD_NOT_AUTHORIZED = 4;
    +    }
    +    // Return status for the request.
    +    Status status = 2;
    +}
    +
    +// PayloadCredentials are used to authorize a payload. 
    +message PayloadCredentials {
    +    // The GUID of the payload.
    +    string guid = 1;
    +
    +    // The secret of the payload.
    +    string secret = 2;
    +}
    \ No newline at end of file
    diff --git a/protos/bosdyn/api/payload_registration_service.proto b/protos/bosdyn/api/payload_registration_service.proto
    index f7890ac27..361f2f42e 100644
    --- a/protos/bosdyn/api/payload_registration_service.proto
    +++ b/protos/bosdyn/api/payload_registration_service.proto
    @@ -20,4 +20,6 @@ service PayloadRegistrationService {
         rpc UpdatePayloadVersion(UpdatePayloadVersionRequest) returns (UpdatePayloadVersionResponse);
         // Get the authentication token information associated with a given payload.
         rpc GetPayloadAuthToken(GetPayloadAuthTokenRequest) returns (GetPayloadAuthTokenResponse);
    +    // Tell the robot whether the specified payload is attached..
    +    rpc UpdatePayloadAttached(UpdatePayloadAttachedRequest) returns (UpdatePayloadAttachedResponse);
     }
    diff --git a/protos/bosdyn/api/power.proto b/protos/bosdyn/api/power.proto
    index 1cec6b310..f79a3952d 100644
    --- a/protos/bosdyn/api/power.proto
    +++ b/protos/bosdyn/api/power.proto
    @@ -13,6 +13,7 @@ option java_outer_classname = "PowerProto";
     import "bosdyn/api/header.proto";
     import "bosdyn/api/lease.proto";
     import "bosdyn/api/license.proto";
    +import "bosdyn/api/robot_state.proto";
     
     // Feedback on the current state of a power command on the robot.
     enum PowerCommandStatus {
    @@ -38,10 +39,10 @@ enum PowerCommandStatus {
         // Inspect EStopState for additional info.
         STATUS_ESTOPPED = 6;
     
    -    // ERROR: Cannot power due to a fault.Inspect FaultState for more info.
    +    // ERROR: Cannot power due to a fault. Inspect FaultState for more info.
         STATUS_FAULTED = 7;
     
    -    // ERROR: Internal error occurred, maybe clear-able by issuing a power off command.
    +    // ERROR: Internal error occurred, may be clear-able by issuing a power off command.
         STATUS_INTERNAL_ERROR = 8;
     
         // ERROR: License check failed. Check license_status field for details.
    @@ -49,6 +50,10 @@ enum PowerCommandStatus {
     
         // ERROR: The Spot hardware is not compatible with the request request.
         INCOMPATIBLE_HARDWARE_ERROR = 10;
    +
    +    // ERROR: Robot has overridden the power command and disabled motor power. In the case
    +    // of a commanded power OFF, robot will report SUCCESS if power is disabled.
    +    STATUS_OVERRIDDEN = 11;
     }
     
     // The PowerCommand request which specifies a change in the robot's motor power.
    @@ -74,7 +79,7 @@ message PowerCommandRequest {
             REQUEST_OFF_PAYLOAD_PORTS = 5;        // Cut power to the payload ports.
             REQUEST_ON_PAYLOAD_PORTS = 6;         // Turn on power to the payload ports.
             REQUEST_OFF_WIFI_RADIO = 7;           // Cut power to the hardware Wi-Fi radio.
    -        REQUEST_ON_WIFI_RADIO = 8;           // Power on the hardware Wi-Fi radio.
    +        REQUEST_ON_WIFI_RADIO = 8;            // Power on the hardware Wi-Fi radio.
     
         }
         Request request = 3;
    @@ -97,6 +102,9 @@ message PowerCommandResponse {
     
         // License check status
         LicenseInfo.Status license_status = 5;
    +
    +    // Optional list of active faults blocking success of the PowerCommandRequest
    +    repeated SystemFault blocking_faults = 6;
     }
     
     // The PowerCommandFeedback request message, which can get the feedback for a specific
    @@ -116,4 +124,7 @@ message PowerCommandFeedbackResponse {
     
         // Current status of specified command.
         PowerCommandStatus status = 2;
    +
    +    // Optional list of active faults blocking success of the PowerCommandRequest
    +    repeated SystemFault blocking_faults = 3;
     }
    diff --git a/protos/bosdyn/api/proto_reference.md b/protos/bosdyn/api/proto_reference.md
    new file mode 100644
    index 000000000..28f1cfd78
    --- /dev/null
    +++ b/protos/bosdyn/api/proto_reference.md
    @@ -0,0 +1,21053 @@
    +
    +
    +
    +
    +
    +
    +# arm_command.proto
    +
    +
    +
    +
    +
    +### ArmCartesianCommand
    +
    +Command the end effector of the arm.  Each axis in the task frame is allowed to be set to
    +position mode (default) or Force mode.  If the axis is set to position, the desired value is read
    +from the pose_trajectory_in_task. If the axis is set to force, the desired value is read from
    +the rench_trajectory. This supports hybrid control of the arm where users can specify, for
    +example, Z to be in force control with X and Y in position control.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmCartesianCommand.Feedback
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| status | [ArmCartesianCommand.Feedback.Status](#bosdyn.api.ArmCartesianCommand.Feedback.Status) | Current status of the pose trajectory. |
    +| measured_pos_tracking_error | [double](#double) | Current linear tracking error of the tool frame [meters]. |
    +| measured_rot_tracking_error | [double](#double) | Current rotational tracking error of the tool frame [radians]. |
    +| measured_pos_distance_to_goal | [double](#double) | Linear distance from the tool to the tool trajectory's end point [meters]. |
    +| measured_rot_distance_to_goal | [double](#double) | Rotational distance from the tool to the trajectory's end point [radians]. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmCartesianCommand.Request
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| root_frame_name | [string](#string) | The root frame is used to set the optional task frame that all trajectories are specified with respect to. If the optional task frame is left un-specified it defaults to the identity transform and the root frame becomes the task frame. |
    +| wrist_tform_tool | [SE3Pose](#bosdyn.api.SE3Pose) | The tool pose relative to the parent link (wrist). Defaults to [0.19557 0 0] [1 0 0 0] a frame with it's origin slightly in front of the gripper's palm plate aligned with wrist's orientation. |
    +| root_tform_task | [SE3Pose](#bosdyn.api.SE3Pose) | The fields below are specified in this optional task frame. If unset it defaults to the identity transform and all quantities are therefore expressed in the root_frame_name. |
    +| pose_trajectory_in_task | [SE3Trajectory](#bosdyn.api.SE3Trajectory) | A 3D pose trajectory for the tool expressed in the task frame, e.g. task_T_tool. This pose trajectory is optional if requesting a pure wrench at the end-effector, otherwise required for position or mixed force/position end-effector requests. |
    +| maximum_acceleration | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Optional Maximum acceleration magnitude of the end-effector. Valid ranges (0, 20] |
    +| max_linear_velocity | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Optional Maximum linear velocity magnitude of the end-effector. (m/s) |
    +| max_angular_velocity | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Optional Maximum angular velocity magnitude of the end-effector. (rad/s) |
    +| max_pos_tracking_error | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Maximum allowable tracking error of the tool frame from the desired trajectory before the arm will stop moving and cancel the rest of the trajectory. When this limit is exceeded, the hand will stay at the pose it was at when it exceeded the tracking error, and any other part of the trajectory specified in the rest of this message will be ignored. max position tracking error in meters |
    +| max_rot_tracking_error | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | max orientation tracking error in radians |
    +| force_remain_near_current_joint_configuration | [bool](#bool) |  |
    +| preferred_joint_configuration | [ArmJointPosition](#bosdyn.api.ArmJointPosition) |  |
    +| x_axis | [ArmCartesianCommand.Request.AxisMode](#bosdyn.api.ArmCartesianCommand.Request.AxisMode) |  |
    +| y_axis | [ArmCartesianCommand.Request.AxisMode](#bosdyn.api.ArmCartesianCommand.Request.AxisMode) |  |
    +| z_axis | [ArmCartesianCommand.Request.AxisMode](#bosdyn.api.ArmCartesianCommand.Request.AxisMode) |  |
    +| rx_axis | [ArmCartesianCommand.Request.AxisMode](#bosdyn.api.ArmCartesianCommand.Request.AxisMode) |  |
    +| ry_axis | [ArmCartesianCommand.Request.AxisMode](#bosdyn.api.ArmCartesianCommand.Request.AxisMode) |  |
    +| rz_axis | [ArmCartesianCommand.Request.AxisMode](#bosdyn.api.ArmCartesianCommand.Request.AxisMode) |  |
    +| wrench_trajectory_in_task | [WrenchTrajectory](#bosdyn.api.WrenchTrajectory) | A force/torque trajectory for the tool expressed in the task frame. This trajectory is optional if requesting a pure pose at the end-effector, otherwise required for force or mixed force/position end-effector requests. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmCommand
    +
    +The synchronized command message for commanding the arm to move.
    +A synchronized commands is one of the possible robot command messages for controlling the robot.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmCommand.Feedback
    +
    +The feedback for the arm command that will provide information on the progress
    +of the command.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| arm_cartesian_feedback | [ArmCartesianCommand.Feedback](#bosdyn.api.ArmCartesianCommand.Feedback) | Feedback for the end-effector Cartesian command. |
    +| arm_joint_move_feedback | [ArmJointMoveCommand.Feedback](#bosdyn.api.ArmJointMoveCommand.Feedback) | Feedback for the joint move command. |
    +| named_arm_position_feedback | [NamedArmPositionsCommand.Feedback](#bosdyn.api.NamedArmPositionsCommand.Feedback) | Feedback for the named position move command. |
    +| arm_velocity_feedback | [ArmVelocityCommand.Feedback](#bosdyn.api.ArmVelocityCommand.Feedback) |  |
    +| arm_gaze_feedback | [GazeCommand.Feedback](#bosdyn.api.GazeCommand.Feedback) | Feedback for the gaze command. |
    +| arm_stop_feedback | [ArmStopCommand.Feedback](#bosdyn.api.ArmStopCommand.Feedback) |  |
    +| arm_drag_feedback | [ArmDragCommand.Feedback](#bosdyn.api.ArmDragCommand.Feedback) | Feedback for the drag command. |
    +| status | [RobotCommandFeedbackStatus.Status](#bosdyn.api.RobotCommandFeedbackStatus.Status) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmCommand.Request
    +
    +The arm request must be one of the basic command primitives.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| arm_cartesian_command | [ArmCartesianCommand.Request](#bosdyn.api.ArmCartesianCommand.Request) | Control the end-effector in Cartesian space. |
    +| arm_joint_move_command | [ArmJointMoveCommand.Request](#bosdyn.api.ArmJointMoveCommand.Request) | Control joint angles of the arm. |
    +| named_arm_position_command | [NamedArmPositionsCommand.Request](#bosdyn.api.NamedArmPositionsCommand.Request) | Move the arm to some predefined configurations. |
    +| arm_velocity_command | [ArmVelocityCommand.Request](#bosdyn.api.ArmVelocityCommand.Request) | Velocity control of the end-effector. |
    +| arm_gaze_command | [GazeCommand.Request](#bosdyn.api.GazeCommand.Request) | Point the gripper at a point in the world. |
    +| arm_stop_command | [ArmStopCommand.Request](#bosdyn.api.ArmStopCommand.Request) | Stop the arm in place with minimal motion. |
    +| arm_drag_command | [ArmDragCommand.Request](#bosdyn.api.ArmDragCommand.Request) | Use the arm to drag something held in the gripper. |
    +| params | [ArmParams](#bosdyn.api.ArmParams) | Any arm parameters to send, common across all arm commands |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmJointMoveCommand
    +
    +Specify a set of joint angles to move the arm.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmJointMoveCommand.Feedback
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| status | [ArmJointMoveCommand.Feedback.Status](#bosdyn.api.ArmJointMoveCommand.Feedback.Status) | Current status of the request. |
    +| planner_status | [ArmJointMoveCommand.Feedback.PlannerStatus](#bosdyn.api.ArmJointMoveCommand.Feedback.PlannerStatus) | Current status of the trajectory planner. |
    +| planned_points | [ArmJointTrajectoryPoint](#bosdyn.api.ArmJointTrajectoryPoint) | Based on the user trajectory, the planned knot points that obey acceleration and velocity constraints. If these knot points don't match the requested knot points, consider increasing velocity/acceleration limits, and/or staying further away from joint position limits. In situations where we've modified you last point, we append a minimum time trajectory (that obeys the velocity and acceleration limits) from the planner's final point to the requested final point. This means that the length of planned_points may be one point larger than the requested. |
    +| time_to_goal | [google.protobuf.Duration](#google.protobuf.Duration) | Returns amount of time remaining until the joints are at the goal position. For multiple point trajectories, this is the time remaining to the final point. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmJointMoveCommand.Request
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| trajectory | [ArmJointTrajectory](#bosdyn.api.ArmJointTrajectory) | Note: Sending a single point empty trajectory will cause the arm to freeze in place. This is an easy way to lock the arm in its current configuration. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmJointPosition
    +
    +Position of our 6 arm joints in radians. If a joint angle is not specified,
    +we will use the joint position at time the message is received on robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| sh0 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) |  |
    +| sh1 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) |  |
    +| el0 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) |  |
    +| el1 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) |  |
    +| wr0 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) |  |
    +| wr1 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmJointTrajectory
    +
    +This allows a user to move the arm's joints directly. Each of the arm's joints will never move
    +faster than maximum_velocity and never accelerate faster than maximum_acceleration. The user can
    +specify a trajectory of joint positions and optional velocities for the arm to follow. The
    +trajectory will be acted upon as follows. If a single trajectory point with no time is provided,
    +the arm will take the joint currently furthest away from the goal pose and plan a minimum time
    +trajectory such that the joint accelerates at maximum_acceleration, coasts at maximum_velocity,
    +and decelerates at maximum_acceleration. The other joints will accelerate at
    +maximum_acceleration, but then coast at a slower speed such that all joints arrive at the goal
    +pose simultaneously with zero velocity. If the user provides trajectory times, the robot will fit
    +a piece-wise cubic trajectory (continuous position and velocity) to the user's requested
    +positions and (optional) velocities. If the requested trajectory is not achievable because it
    +will violate position limits or the maximum_velocity or maximum_acceleration, the robot will pick
    +a trajectory that is as close as possible to the user requested without violating velocity or
    +acceleration limits.
    +
    +If the robot is not hitting the desired trajectory, try increasing the time between knot points,
    +increasing the max velocity and acceleration, or only specifying joint position goals without a
    +velocity
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| points | [ArmJointTrajectoryPoint](#bosdyn.api.ArmJointTrajectoryPoint) | The points in our trajectory. (positions, (optional) velocity, (optional) time) |
    +| reference_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | All trajectory points specify times relative to this reference time. The reference time should be in robot clock. If this field is not included, this time will be the receive time of the command. |
    +| maximum_velocity | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The maximum velocity in rad/s that any joint is allowed to achieve. If this field is not set, a default value will be used. |
    +| maximum_acceleration | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The maximum acceleration in rad/s^2 that any joint is allowed to achieve. If this field is not set, a default value will be used. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmJointTrajectoryPoint
    +
    +A set of joint angles and velocities that can be used as a point within a joint trajectory.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| position | [ArmJointPosition](#bosdyn.api.ArmJointPosition) | Desired joint angles in radians |
    +| velocity | [ArmJointVelocity](#bosdyn.api.ArmJointVelocity) | Optional desired joint velocities in radians / sec |
    +| time_since_reference | [google.protobuf.Duration](#google.protobuf.Duration) | The time since reference at which we wish to achieve this position / velocity |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmJointVelocity
    +
    +Velocity of our 6 arm joints in radians / second. If a velocity
    +for a joint is specified, velocities for all joints we are
    +trying to move must be specified.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| sh0 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) |  |
    +| sh1 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) |  |
    +| el0 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) |  |
    +| el1 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) |  |
    +| wr0 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) |  |
    +| wr1 | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmParams
    +
    +Parameters common across arm commands.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| disable_body_force_limiter | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Whether or not to disable the body force limiter running on the robot. By default, this is / on, and the chance that the body falls over because the arm makes contact in the world is / low. If this is purposely disabled (by setting disable_body_force_limiter to True), the arm / may be able to accelerate faster, and apply more force to the world and to objects than usual, / but there is also added risk of the robot falling over. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmStopCommand
    +
    +Stop the arm applying minimal forces to the world. For example, if the arm is in the  middle of
    +opening a heavy door and a stop command is sent, the arm will comply and let the door close.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmStopCommand.Feedback
    +
    +Stop command provides no feedback
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmStopCommand.Request
    +
    +Stop command takes no arguments.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmVelocityCommand
    +
    +When controlling the arm with a joystick, because of latency it can often be better to send
    +velocity commands rather than position commands.  Both linear and angular velocity can be
    +specified.  The linear velocity can be specified in a cylindrical frame around the shoulder or
    +with a specified frame.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmVelocityCommand.CartesianVelocity
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| frame_name | [string](#string) | The frame to express our velocities in |
    +| velocity_in_frame_name | [Vec3](#bosdyn.api.Vec3) | The x-y-z velocity of the hand (m/s) with respect to the frame |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmVelocityCommand.CylindricalVelocity
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| linear_velocity | [CylindricalCoordinate](#bosdyn.api.CylindricalCoordinate) | The linear velocities for the end-effector are specified in unitless cylindrical / coordinates. The origin of the cylindrical coordinate system is the base of the arm / (shoulder). The Z-axis is aligned with gravity, and the X-axis is the unit vector from / the shoulder to the hand-point. This construction allows for 'Z'-axis velocities to / raise/lower the hand, 'R'-axis velocities will cause the hand to move towards/away from / the shoulder, and 'theta'-axis velocities will cause the hand to travel / clockwise/counter-clockwise around the shoulder. Lastly, the command is unitless, with / values for each axis specified in the range [-1, 1]. A value of 0 denotes no velocity / and values of +/- 1 denote maximum velocity (see max_linear_velocity). |
    +| max_linear_velocity | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The maximum velocity in meters / second for the hand. / If unset and default value of 0 received, will set max_linear_velocity to 0.5 m/s. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmVelocityCommand.Feedback
    +
    +ArmVelocityCommand provides no feedback
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmVelocityCommand.Request
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| cylindrical_velocity | [ArmVelocityCommand.CylindricalVelocity](#bosdyn.api.ArmVelocityCommand.CylindricalVelocity) |  |
    +| cartesian_velocity | [ArmVelocityCommand.CartesianVelocity](#bosdyn.api.ArmVelocityCommand.CartesianVelocity) |  |
    +| angular_velocity_of_hand_rt_odom_in_hand | [Vec3](#bosdyn.api.Vec3) | The angular velocity of the hand frame measured with respect to the odom frame, expressed in the hand frame. A 'X' rate will cause the hand to rotate about its x-axis, e.g. the final wrist twist joint will rotate. And similarly, 'Y' and 'Z' rates will cause the hand to rotate about its y and z axis respectively. \ The units should be rad/sec. |
    +| maximum_acceleration | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Optional maximum acceleration magnitude of the end-effector. (m/s^2) |
    +| end_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | The timestamp (in robot time) by which a command must finish executing. This is a required field and used to prevent runaway commands. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GazeCommand
    +
    +Move the hand in such a way to point it at a position in the world.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### GazeCommand.Feedback
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| status | [GazeCommand.Feedback.Status](#bosdyn.api.GazeCommand.Feedback.Status) | Current status of the command. |
    +| gazing_at_target | [bool](#bool) | If we are gazing at the target Rotation from the current gaze point to the trajectory's end [radians] |
    +| gaze_to_target_rotation_measured | [float](#float) |  |
    +| hand_position_at_goal | [bool](#bool) | If the hand's position is at the goal. Distance from the hand's current position to the trajectory's end [meters] |
    +| hand_distance_to_goal_measured | [float](#float) |  |
    +| hand_roll_at_goal | [bool](#bool) | If the hand's roll is at the goal. Rotation from the current hand position to the desired roll at the trajectory's end [radians] |
    +| hand_roll_to_target_roll_measured | [float](#float) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GazeCommand.Request
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| target_trajectory_in_frame1 | [Vec3Trajectory](#bosdyn.api.Vec3Trajectory) | Point(s) to look at expressed in frame1. |
    +| frame1_name | [string](#string) |  |
    +| tool_trajectory_in_frame2 | [SE3Trajectory](#bosdyn.api.SE3Trajectory) | Optional, desired pose of the tool expressed in frame2. Will be constrained to 'look at' the target regardless of the requested orientation. |
    +| frame2_name | [string](#string) |  |
    +| wrist_tform_tool | [SE3Pose](#bosdyn.api.SE3Pose) | The transformation of the tool pose relative to the parent link (wrist). If the field is left unset, the transform will default to: The position is wrist_tform_hand.position() [20 cm translation in wrist x]. The rotation is wrist_tform_hand_camera.rotation() [-9 degree pitch about wrist y]. |
    +| target_trajectory_initial_velocity | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Optional velocity to move the target along the shortest path from the gaze's starting position to the first point in the target trajectory. |
    +| maximum_acceleration | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Optional Maximum acceleration magnitude of the end-effector. Valid ranges (0, 20] |
    +| max_linear_velocity | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Optional Maximum linear velocity magnitude of the end-effector. (m/s) |
    +| max_angular_velocity | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Optional Maximum angular velocity magnitude of the end-effector. (rad/s) |
    +
    +
    +
    +
    +
    +
    +
    +
    +### NamedArmPositionsCommand
    +
    +Command the arm move to a predefined configuration.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### NamedArmPositionsCommand.Feedback
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| status | [NamedArmPositionsCommand.Feedback.Status](#bosdyn.api.NamedArmPositionsCommand.Feedback.Status) | Current status of the request. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### NamedArmPositionsCommand.Request
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| position | [NamedArmPositionsCommand.Positions](#bosdyn.api.NamedArmPositionsCommand.Positions) |  |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### ArmCartesianCommand.Feedback.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | STATUS_UNKNOWN should never be used. If used, an internal error has happened. |
    +| STATUS_TRAJECTORY_COMPLETE | 1 | Tool frame has reached the end of the trajectory within tracking error bounds. |
    +| STATUS_IN_PROGRESS | 2 | Robot is attempting to reach the target. |
    +| STATUS_TRAJECTORY_CANCELLED | 3 | Tool frame exceeded maximum allowable tracking error from the desired trajectory. |
    +| STATUS_TRAJECTORY_STALLED | 4 | The arm has stopped making progress to the goal. Note, this does not cancel the trajectory. For example, if the requested goal is too far away, walking the base robot closer to the goal will cause the arm to continue along the trajectory once the goal point is inside the workspace. |
    +
    +
    +
    +
    +
    +### ArmCartesianCommand.Request.AxisMode
    +
    +If an axis is set to position mode (default), read desired from SE3Trajectory trajectory
    +command.  If mode is set to Force, read desired from WrenchTrajectory wrench_trajectory
    +command.  This supports hybrid control of the arm where users can specify, for example, Z
    +to be in force control with X and Y in position control.  The elements are expressed in
    +the same task_frame as the trajectories.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| AXIS_MODE_POSITION | 0 |  |
    +| AXIS_MODE_FORCE | 1 |  |
    +
    +
    +
    +
    +
    +### ArmJointMoveCommand.Feedback.PlannerStatus
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| PLANNER_STATUS_UNKNOWN | 0 | PLANNER_STATUS_UNKNOWN should never be used. If used, an internal error has happened. |
    +| PLANNER_STATUS_SUCCESS | 1 | A solution passing through the desired points and obeying the constraints was found. |
    +| PLANNER_STATUS_MODIFIED | 2 | The planner had to modify the desired points in order to obey the constraints. For example, if you specify a 1 point trajectory, and tell it to get there in a really short amount of time, but haven't set a high allowable max velocity / acceleration, the planner will do its best to get as close as possible to the final point, but won't reach it. In situations where we've modified you last point, we append a minimum time trajectory (that obeys the velocity and acceleration limits) from the planner's final point to the requested final point. |
    +| PLANNER_STATUS_FAILED | 3 | Failed to compute a valid trajectory, will go to first point instead. It is possible that our optimizer till fail to solve the problem instead of returning a sub-optimal solution. This is un-likely to occur. |
    +
    +
    +
    +
    +
    +### ArmJointMoveCommand.Feedback.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | STATUS_UNKNOWN should never be used. If used, an internal error has happened |
    +| STATUS_COMPLETE | 1 | The arm is at the desired configuration. |
    +| STATUS_IN_PROGRESS | 2 | Robot is re-configuring arm to get to desired configuration. |
    +
    +
    +
    +
    +
    +### GazeCommand.Feedback.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | STATUS_UNKNOWN should never be used. If used, an internal error has happened. |
    +| STATUS_TRAJECTORY_COMPLETE | 1 | Robot is gazing at the target at the end of the trajectory. |
    +| STATUS_IN_PROGRESS | 2 | Robot is re-configuring arm to gaze at the target. |
    +| STATUS_TOOL_TRAJECTORY_STALLED | 3 | The arm has stopped making progress to the goal pose for the tool. Note, this does not cancel the trajectory. For example, if the requested goal is too far away, walking the base robot closer to the goal will cause the arm to continue along the trajectory once it can continue. |
    +
    +
    +
    +
    +
    +### NamedArmPositionsCommand.Feedback.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | STATUS_UNKNOWN should never be used. If used, an internal error has happened. |
    +| STATUS_COMPLETE | 1 | The arm is at the desired configuration. |
    +| STATUS_IN_PROGRESS | 2 | Robot is re-configuring arm to get to desired configuration. |
    +| STATUS_STALLED_HOLDING_ITEM | 3 | Some positions may refuse to execute if the gripper is holding an item, for example stow. |
    +
    +
    +
    +
    +
    +### NamedArmPositionsCommand.Positions
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| POSITIONS_UNKNOWN | 0 | Invalid request; do not use. |
    +| POSITIONS_CARRY | 1 | The carry position is a damped, force limited position close to stow, with the hand slightly in front of the robot. |
    +| POSITIONS_READY | 2 | Move arm to ready position. The ready position is defined with the hand directly in front of and slightly above the body, with the hand facing forward in the robot body +X direction. |
    +| POSITIONS_STOW | 3 | Stow the arm, safely. If the robot is holding something, it will freeze the arm instead of stowing. Overriding the carry_state to CARRY_STATE_CARRIABLE_AND_STOWABLE, will allow the robot to stow the arm while grasping an item. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# arm_surface_contact.proto
    +
    +
    +
    +
    +
    +### ArmSurfaceContact
    +
    +ArmSurfaceContact lets you accurately move the robot's arm in the world while having some ability
    +to perform force control.  This mode is useful for drawing, wiping, and other similar behaviors.
    +
    +The message is similar to the ArmCartesianCommand message, which you can look at for additional
    +details.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmSurfaceContact.Feedback
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmSurfaceContact.Request
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| root_frame_name | [string](#string) | The root frame is used to set the optional task frame that all trajectories are specified with respect to. If the optional task frame is left un-specified it defaults to the identity transform and the root frame becomes the task frame. |
    +| wrist_tform_tool | [SE3Pose](#bosdyn.api.SE3Pose) | The tool pose relative to the parent link (wrist). Defaults to [0.19557 0 0] [1 0 0 0] a frame with it's origin slightly in front of the gripper's palm plate aligned with wrists orientation. |
    +| root_tform_task | [SE3Pose](#bosdyn.api.SE3Pose) | The fields below are specified in this optional task frame. If unset int defaults to the identity transform and all quantities are therefore expressed in the root_frame_name. |
    +| pose_trajectory_in_task | [SE3Trajectory](#bosdyn.api.SE3Trajectory) | A 3D pose trajectory for the tool expressed in the task frame, e.g. task_T_tool. This pose trajectory is optional if requesting a pure wrench at the end-effector, otherwise required for position or mixed force/position end-effector requests. |
    +| maximum_acceleration | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Optional Maximum acceleration magnitude of the end-effector. Valid ranges (0, 20] |
    +| max_linear_velocity | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Optional Maximum linear velocity magnitude of the end-effector. (m/s) |
    +| max_angular_velocity | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Optional Maximum angular velocity magnitude of the end-effector. (rad/s) |
    +| max_pos_tracking_error | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Maximum allowable tracking error of the tool frame from the desired trajectory before the arm will stop moving and cancel the rest of the trajectory. When this limit is exceeded, the hand will stay at the pose it was at when it exceeded the tracking error, and any other part of the trajectory specified in the rest of this message will be ignored. max position tracking error in meters |
    +| max_rot_tracking_error | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | max orientation tracking error in radians |
    +| force_remain_near_current_joint_configuration | [bool](#bool) |  |
    +| preferred_joint_configuration | [ArmJointPosition](#bosdyn.api.ArmJointPosition) |  |
    +| x_axis | [ArmSurfaceContact.Request.AxisMode](#bosdyn.api.ArmSurfaceContact.Request.AxisMode) |  |
    +| y_axis | [ArmSurfaceContact.Request.AxisMode](#bosdyn.api.ArmSurfaceContact.Request.AxisMode) |  |
    +| z_axis | [ArmSurfaceContact.Request.AxisMode](#bosdyn.api.ArmSurfaceContact.Request.AxisMode) |  |
    +| press_force_percentage | [Vec3](#bosdyn.api.Vec3) | Amount of force to use on each axis, from 0 (no force) to 1.0 (maximum force), can also be negative. Full range: [-1.0, 1.0] |
    +| xy_admittance | [ArmSurfaceContact.Request.AdmittanceSetting](#bosdyn.api.ArmSurfaceContact.Request.AdmittanceSetting) | Admittance settings for each axis in the admittance frame. |
    +| z_admittance | [ArmSurfaceContact.Request.AdmittanceSetting](#bosdyn.api.ArmSurfaceContact.Request.AdmittanceSetting) |  |
    +| xy_to_z_cross_term_admittance | [ArmSurfaceContact.Request.AdmittanceSetting](#bosdyn.api.ArmSurfaceContact.Request.AdmittanceSetting) | Cross term, making force in the XY axis cause movement in the z-axis. By default is OFF Setting this value will make the arm move in the negative Z-axis whenever it feels force in the XY axis. |
    +| bias_force_ewrt_body | [Vec3](#bosdyn.api.Vec3) | Specifies a force that the body should expect to feel. This allows the robot to "lean into" an external force. Be careful using this field, because if you lie to the robot, it can fall over. |
    +| gripper_command | [ClawGripperCommand.Request](#bosdyn.api.ClawGripperCommand.Request) | Gripper control |
    +| is_robot_following_hand | [bool](#bool) | Set to true to have robot is walk around to follow the hand. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### ArmSurfaceContact.Request.AdmittanceSetting
    +
    +Parameters for controlling admittance.  By default, the robot will
    +stop moving the arm when it encounters resistance.  You can control that reaction to
    +make the robot stiffer or less stiff by changing the parameters.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| ADMITTANCE_SETTING_UNKNOWN | 0 |  |
    +| ADMITTANCE_SETTING_OFF | 1 | No admittance. |
    +| ADMITTANCE_SETTING_NORMAL | 2 | Normal reaction to touching things in the world |
    +| ADMITTANCE_SETTING_LOOSE | 3 | Robot will not push very hard against objects |
    +| ADMITTANCE_SETTING_STIFF | 4 | Robot will push hard against the world |
    +| ADMITTANCE_SETTING_VERY_STIFF | 5 | Robot will push very hard against the world |
    +
    +
    +
    +
    +
    +### ArmSurfaceContact.Request.AxisMode
    +
    +If an axis is set to position mode (default), read desired from SE3Trajectory command.
    +If mode is set to force, use the "press_force_percentage" field to determine force.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| AXIS_MODE_POSITION | 0 |  |
    +| AXIS_MODE_FORCE | 1 |  |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# arm_surface_contact_service.proto
    +
    +
    +
    +
    +
    +### ArmSurfaceContactCommand
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| lease | [Lease](#bosdyn.api.Lease) | The Lease to show ownership of the robot. |
    +| request | [ArmSurfaceContact.Request](#bosdyn.api.ArmSurfaceContact.Request) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmSurfaceContactResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +
    +
    +
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### ArmSurfaceContactService
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| ArmSurfaceContact | [ArmSurfaceContactCommand](#bosdyn.api.ArmSurfaceContactCommand) | [ArmSurfaceContactResponse](#bosdyn.api.ArmSurfaceContactResponse) |  |
    +
    + 
    +
    +
    +
    +
    +
    +# auth.proto
    +
    +
    +
    +
    +
    +### GetAuthTokenRequest
    +
    +The GetAuthToken request message includes login information for the robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| username | [string](#string) | Username to authenticate with. Must be set if password is set. |
    +| password | [string](#string) | Password to authenticate with. Not neccessary if token is set. |
    +| token | [string](#string) | Token to authenticate with. Can be used in place of the password, to re-mint a token. |
    +| application_token | [string](#string) | Deprecated as of 2.0.1. Application Token for authenticating with robots on older releases. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetAuthTokenResponse
    +
    +The GetAuthToken response message includes an authentication token if the login information
    +is correct and succeeds.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) |  |
    +| status | [GetAuthTokenResponse.Status](#bosdyn.api.GetAuthTokenResponse.Status) | The status of the grpc GetAuthToken request. |
    +| token | [string](#string) | Token data. Only specified if status == STATUS_OK. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### GetAuthTokenResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | STATUS_UNKNOWN should never be used. If used, an internal error has happend. |
    +| STATUS_OK | 1 | STATUS_OK indicates that authentication has succeeded. The 'token' field field will be populated with a session token that can be used to authenticate the user. |
    +| STATUS_INVALID_LOGIN | 2 | STATUS_INVALID_LOGIN indicates that authentication has failed since an invalid username and/or password were provided. |
    +| STATUS_INVALID_TOKEN | 3 | STATUS_INVALID_TOKEN indicates that authentication has failed since the 'token' provided in the request is invalid. Reasons for the token being invalid could be because it has expired, because it is improperly formed, for the wrong robot, the user that the token is for has changed a password, or many other reasons. Clients should use username/password-based authentication when refreshing the token fails. |
    +| STATUS_TEMPORARILY_LOCKED_OUT | 4 | STATUS_TEMPORARILY_LOCKED_OUT indicates that authentication has failed since authentication for the user is temporarily locked out due to too many unsuccessful attempts. Any new authentication attempts should be delayed so they may happen after the lock out period ends. |
    +| STATUS_INVALID_APPLICATION_TOKEN | 5 | STATUS_INVALID_APPLICATION_TOKEN indicates that the 'application_token' field in the request was invalid. |
    +| STATUS_EXPIRED_APPLICATION_TOKEN | 6 | STATUS_EXPIRED_APPLICATION_TOKEN indicates that the 'application_token' field in the request was valid, but has expired. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# auth_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### AuthService
    +
    +The AuthService provides clients the ability to convert a user/password pair into a token. The
    +token can then be added to the http2 headers for future requests in order to establish the
    +identity of the requester.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| GetAuthToken | [GetAuthTokenRequest](#bosdyn.api.GetAuthTokenRequest) | [GetAuthTokenResponse](#bosdyn.api.GetAuthTokenResponse) | Request to get the auth token for the robot. |
    +
    + 
    +
    +
    +
    +
    +
    +# auto_return/auto_return.proto
    +
    +
    +
    +
    +
    +### ConfigureRequest
    +
    +Configure the AutoReturn system.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| leases | [bosdyn.api.Lease](#bosdyn.api.Lease) | Leases that AutoReturn may use to accomplish its goals when AutoReturn automatically triggers. If left empty, AutoReturn will not automatically trigger. |
    +| params | [Params](#bosdyn.api.auto_return.Params) | Parameters to use when AutoReturn automatically triggers. |
    +| clear_buffer | [bool](#bool) | Forget any buffered locations the robot may be remembering. Defaults to false. Set to true if the robot has just crossed an area it should not traverse in AutoReturn. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ConfigureResponse
    +
    +Response to a configuration request.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [ConfigureResponse.Status](#bosdyn.api.auto_return.ConfigureResponse.Status) | Return status for the request. |
    +| invalid_params | [Params](#bosdyn.api.auto_return.Params) | If status is STATUS_INVALID_PARAMS, this contains the settings that were invalid. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetConfigurationRequest
    +
    +Ask for the current configuration.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetConfigurationResponse
    +
    +Response to a "get configuration" request.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| enabled | [bool](#bool) | A simple yes/no, will AutoReturn automatically trigger. |
    +| request | [ConfigureRequest](#bosdyn.api.auto_return.ConfigureRequest) | The most recent successful ConfigureRequest. Will be empty if service has not successfully been configured. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Params
    +
    +Parameters to AutoReturn actions.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| mobility_params | [google.protobuf.Any](#google.protobuf.Any) | Robot-specific mobility parameters to use. For example, see bosdyn.api.spot.MobilityParams. |
    +| max_displacement | [float](#float) | Allow AutoReturn to move the robot this far in the XY plane from the location where AutoReturn started. Travel along the Z axis (which is gravity-aligned) does not count. Must be > 0.
    +
    +meters |
    +| max_duration | [google.protobuf.Duration](#google.protobuf.Duration) | Optionally specify the maximum amount of time AutoReturn can take. If this duration is exceeded, AutoReturn will stop trying to move the robot. Omit to try indefinitely. Robot may become stuck and never take other comms loss actions! |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StartRequest
    +
    +Start AutoReturn behavior now.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StartResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### ConfigureResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 |  |
    +| STATUS_OK | 1 |  |
    +| STATUS_INVALID_PARAMS | 2 |  |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# auto_return/auto_return_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### AutoReturnService
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| Configure | [ConfigureRequest](#bosdyn.api.auto_return.ConfigureRequest) | [ConfigureResponse](#bosdyn.api.auto_return.ConfigureResponse) | Configure the service. |
    +| GetConfiguration | [GetConfigurationRequest](#bosdyn.api.auto_return.GetConfigurationRequest) | [GetConfigurationResponse](#bosdyn.api.auto_return.GetConfigurationResponse) | Get the current configuration. |
    +| Start | [StartRequest](#bosdyn.api.auto_return.StartRequest) | [StartResponse](#bosdyn.api.auto_return.StartResponse) | Start AutoReturn now. |
    +
    + 
    +
    +
    +
    +
    +
    +# basic_command.proto
    +
    +
    +
    +
    +
    +### ArmDragCommand
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmDragCommand.Feedback
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| status | [ArmDragCommand.Feedback.Status](#bosdyn.api.ArmDragCommand.Feedback.Status) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ArmDragCommand.Request
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### BatteryChangePoseCommand
    +
    +Get the robot into a convenient pose for changing the battery
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### BatteryChangePoseCommand.Feedback
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| status | [BatteryChangePoseCommand.Feedback.Status](#bosdyn.api.BatteryChangePoseCommand.Feedback.Status) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### BatteryChangePoseCommand.Request
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| direction_hint | [BatteryChangePoseCommand.Request.DirectionHint](#bosdyn.api.BatteryChangePoseCommand.Request.DirectionHint) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ConstrainedManipulationCommand
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### ConstrainedManipulationCommand.Feedback
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| status | [ConstrainedManipulationCommand.Feedback.Status](#bosdyn.api.ConstrainedManipulationCommand.Feedback.Status) |  |
    +| desired_wrench_odom_frame | [Wrench](#bosdyn.api.Wrench) | Desired wrench in odom world frame, applied at hand frame origin |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ConstrainedManipulationCommand.Request
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| frame_name | [string](#string) | Frame that the initial wrench will be expressed in |
    +| init_wrench_direction_in_frame_name | [Wrench](#bosdyn.api.Wrench) | Direction of the initial wrench to be applied Depending on the task, either the force vector or the torque vector are required to be specified. The required vector should not have a magnitude of zero and will be normalized to 1. For tasks that require the force vector, the torque vector can still be specified as a non-zero vector if it is a good guess of the axis of rotation of the task. (for e.g. TASK_TYPE_SE3_ROTATIONAL_TORQUE task types.) Note that if both vectors are non-zero, they have to be perpendicular. Once the constrained manipulation system estimates the constraint, the init_wrench_direction and frame_name will no longer be used. |
    +| tangential_speed | [double](#double) | Recommended values are in the range of [-4, 4] m/s |
    +| rotational_speed | [double](#double) | Recommended values are in the range of [-4, 4] rad/s |
    +| force_limit | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The limit on the force that is applied on any translation direction Value must be positive If unspecified, a default value of 40 N will be used. |
    +| torque_limit | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The limit on the torque that is applied on any rotational direction Value must be positive If unspecified, a default value of 4 Nm will be used. |
    +| task_type | [ConstrainedManipulationCommand.Request.TaskType](#bosdyn.api.ConstrainedManipulationCommand.Request.TaskType) |  |
    +| end_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | The timestamp (in robot time) by which a command must finish executing. This is a required field and used to prevent runaway commands. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### FollowArmCommand
    +
    +The base will move in response to the hand's location, allow the arm to reach beyond
    +its current workspace.  If the hand is moved forward, the body will begin walking
    +forward to keep the base at the desired offset from the hand.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### FollowArmCommand.Feedback
    +
    +FollowArmCommand commands provide no feedback.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### FollowArmCommand.Request
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| body_offset_from_hand | [Vec3](#bosdyn.api.Vec3) | Optional body offset from the hand. For example, to have the body 0.75 meters behind the hand, use (0.75, 0, 0) |
    +| disable_walking | [bool](#bool) | Optional. If true, the body will be restricted to body orientation offsets only. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### FreezeCommand
    +
    +Freeze all joints at their current positions (no balancing control).
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### FreezeCommand.Feedback
    +
    +Freeze command provides no feedback.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### FreezeCommand.Request
    +
    +Freeze command request takes no additional arguments.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### RobotCommandFeedbackStatus
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### SE2TrajectoryCommand
    +
    +Move along a trajectory in 2D space.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### SE2TrajectoryCommand.Feedback
    +
    +The SE2TrajectoryCommand will provide feedback on whether or not the robot has reached the
    +final point of the trajectory.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| status | [SE2TrajectoryCommand.Feedback.Status](#bosdyn.api.SE2TrajectoryCommand.Feedback.Status) | Current status of the command. |
    +| body_movement_status | [SE2TrajectoryCommand.Feedback.BodyMovementStatus](#bosdyn.api.SE2TrajectoryCommand.Feedback.BodyMovementStatus) | Current status of how the body is moving |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SE2TrajectoryCommand.Request
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| end_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | The timestamp (in robot time) by which a command must finish executing. This is a required field and used to prevent runaway commands. |
    +| se2_frame_name | [string](#string) | The name of the frame that trajectory is relative to. The trajectory must be expressed in a gravity aligned frame, so either "vision", "odom", or "body". Any other provided se2_frame_name will be rejected and the trajectory command will not be exectuted. |
    +| trajectory | [SE2Trajectory](#bosdyn.api.SE2Trajectory) | The trajectory that the robot should follow, expressed in the frame identified by se2_frame_name. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SE2VelocityCommand
    +
    +Move the robot at a specific SE2 velocity for a fixed amount of time.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### SE2VelocityCommand.Feedback
    +
    +Planar velocity commands provide no feedback.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### SE2VelocityCommand.Request
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| end_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | The timestamp (in robot time) by which a command must finish executing. This is a required field and used to prevent runaway commands. |
    +| se2_frame_name | [string](#string) | The name of the frame that velocity and slew_rate_limit are relative to. The trajectory must be expressed in a gravity aligned frame, so either "vision", "odom", or "flat_body". Any other provided se2_frame_name will be rejected and the velocity command will not be executed. |
    +| velocity | [SE2Velocity](#bosdyn.api.SE2Velocity) | Desired planar velocity of the robot body relative to se2_frame_name. |
    +| slew_rate_limit | [SE2Velocity](#bosdyn.api.SE2Velocity) | If set, limits how quickly velocity can change relative to se2_frame_name. Otherwise, robot may decide to limit velocities using default settings. These values should be non-negative. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SafePowerOffCommand
    +
    +Get robot into a position where it is safe to power down, then power down. If the robot has
    +fallen, it will power down directly. If the robot is not in a safe position, it will get to a
    +safe position before powering down. The robot will not power down until it is in a safe state.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### SafePowerOffCommand.Feedback
    +
    +The SafePowerOff will provide feedback on whether or not it has succeeded in powering off
    +the robot yet.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| status | [SafePowerOffCommand.Feedback.Status](#bosdyn.api.SafePowerOffCommand.Feedback.Status) | Current status of the command. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SafePowerOffCommand.Request
    +
    +SafePowerOff command request takes no additional arguments.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### SelfRightCommand
    +
    +Move the robot into a "ready" position from which it can sit or stand up.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### SelfRightCommand.Feedback
    +
    +SelfRight command request provides no feedback.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### SelfRightCommand.Request
    +
    +SelfRight command request takes no additional arguments.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### SitCommand
    +
    +Sit the robot down in its current position.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### SitCommand.Feedback
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| status | [SitCommand.Feedback.Status](#bosdyn.api.SitCommand.Feedback.Status) | Current status of the command. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SitCommand.Request
    +
    +Sit command request takes no additional arguments.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### Stance
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| se2_frame_name | [string](#string) | The frame name which the desired foot_positions are described in. |
    +| foot_positions | [Stance.FootPositionsEntry](#bosdyn.api.Stance.FootPositionsEntry) | Map of foot name to its x,y location in specified frame. Required positions for spot: "fl", "fr", "hl", "hr". |
    +| accuracy | [float](#float) | Required foot positional accuracy in meters Advised = 0.05 ( 5cm) Minimum = 0.02 ( 2cm) Maximum = 0.10 (10cm) |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Stance.FootPositionsEntry
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| key | [string](#string) |  |
    +| value | [Vec2](#bosdyn.api.Vec2) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StanceCommand
    +
    +Precise foot placement
    +This can be used to reposition the robots feet in place.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### StanceCommand.Feedback
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| status | [StanceCommand.Feedback.Status](#bosdyn.api.StanceCommand.Feedback.Status) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StanceCommand.Request
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| end_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | The timestamp (in robot time) by which a command must finish executing. / This is a required field and used to prevent runaway commands. |
    +| stance | [Stance](#bosdyn.api.Stance) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StandCommand
    +
    +The stand the robot up in its current position.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### StandCommand.Feedback
    +
    +The StandCommand will provide feedback on whether or not the robot has finished
    +standing up.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| status | [StandCommand.Feedback.Status](#bosdyn.api.StandCommand.Feedback.Status) | Current status of the command. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StandCommand.Request
    +
    +Stand command request takes no additional arguments.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### StopCommand
    +
    +Stop the robot in place with minimal motion.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### StopCommand.Feedback
    +
    +Stop command provides no feedback.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### StopCommand.Request
    +
    +Stop command request takes no additional arguments.
    +
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### ArmDragCommand.Feedback.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | STATUS_UNKNOWN should never be used. If used, an internal error has happened. |
    +| STATUS_DRAGGING | 1 | Robot is dragging. |
    +| STATUS_GRASP_FAILED | 2 | Robot is not dragging because grasp failed. This could be due to a lost grasp during a drag, or because the gripper isn't in a good position at the time of request. You'll have to reposition or regrasp and then send a new drag request to overcome this type of error. Note: When requesting drag, make sure the gripper is positioned in front of the robot (not to the side of or above the robot body). |
    +| STATUS_OTHER_FAILURE | 3 | Robot is not dragging for another reason. This might be because the gripper isn't holding an item. You can continue dragging once you resolve this type of error (i.e. by sending an ApiGraspOverride request). Note: When requesting drag, be sure to that the robot knows it's holding something (or use APIGraspOverride to OVERRIDE_HOLDING). |
    +
    +
    +
    +
    +
    +### BatteryChangePoseCommand.Feedback.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 |  |
    +| STATUS_COMPLETED | 1 | Robot is finished rolling |
    +| STATUS_IN_PROGRESS | 2 | Robot still in process of rolling over |
    +| STATUS_FAILED | 3 | Robot has failed to roll onto its side |
    +
    +
    +
    +
    +
    +### BatteryChangePoseCommand.Request.DirectionHint
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| HINT_UNKNOWN | 0 | Unknown direction, just hold still |
    +| HINT_RIGHT | 1 | Roll over right (right feet end up under the robot) |
    +| HINT_LEFT | 2 | Roll over left (left feet end up under the robot) |
    +
    +
    +
    +
    +
    +### ConstrainedManipulationCommand.Feedback.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | STATUS_UNKNOWN should never be used. If used, an internal error has happened. |
    +| STATUS_RUNNING | 1 | Constrained manipulation is working as expected |
    +| STATUS_ARM_IS_STUCK | 2 | Arm is stuck, either force is being applied in a direction where the affordance can't move or not enough force is applied |
    +| STATUS_GRASP_IS_LOST | 3 | The grasp was lost. In this situation, constrained manipulation will stop applying force, and will hold the last position. |
    +
    +
    +
    +
    +
    +### ConstrainedManipulationCommand.Request.TaskType
    +
    +Geometrical category of a task. See the constrained_manipulation_helper function
    +for examples of each of these categories. For e.g. SE3_CIRCLE_FORCE_TORQUE corresponds
    +to lever type objects.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| TASK_TYPE_UNKNOWN | 0 |  |
    +| TASK_TYPE_SE3_CIRCLE_FORCE_TORQUE | 1 | This task type corresponds to circular tasks where both the end-effector position and orientation rotate about a circle to manipulate. The constrained manipulation logic will generate forces and torques in this case. Example tasks are: A lever or a ball valve with a solid grasp This task type will require an initial force vector specified in init_wrench_direction_in_frame_name. A torque vector can be specified as well if a good initial guess of the axis of rotation of the task is available. |
    +| TASK_TYPE_R3_CIRCLE_EXTRADOF_FORCE | 2 | This task type corresponds to circular tasks that have an extra degree of freedom. In these tasks the end-effector position rotates about a circle but the orientation does not need to follow a circle (can remain fixed). The constrained manipulation logic will generate translational forces in this case. Example tasks are: A crank that has a loose handle and moves in a circle and the end-effector is free to rotate about the handle in one direction. This task type will require an initial force vector specified in init_wrench_direction_in_frame_name. |
    +| TASK_TYPE_SE3_ROTATIONAL_TORQUE | 3 | This task type corresponds to purely rotational tasks. In these tasks the orientation of the end-effector follows a circle, and the position remains fixed. The robot will apply a torque at the end-effector in these tasks. Example tasks are: rotating a knob or valve that does not have a lever arm. This task type will require an initial torque vector specified in init_wrench_direction_in_frame_name. |
    +| TASK_TYPE_R3_CIRCLE_FORCE | 4 | This task type corresponds to circular tasks where the end-effector position and orientation rotate about a circle but the orientation does always strictly follow the circle due to slips. The constrained manipulation logic will generate translational forces in this case. Example tasks are: manipulating a cabinet where the grasp on handle is not very rigid or can often slip. This task type will require an initial force vector specified in init_wrench_direction_in_frame_name. |
    +| TASK_TYPE_R3_LINEAR_FORCE | 5 | This task type corresponds to linear tasks where the end-effector position moves in a line but the orientation does not need to change. The constrained manipulation logic will generate a force in this case. Example tasks are: A crank that has a loose handle, or manipulating a cabinet where the grasp of the handle is loose and the end-effector is free to rotate about the handle in one direction. This task type will require an initial force vector specified in init_wrench_direction_in_frame_name. |
    +| TASK_TYPE_HOLD_POSE | 6 | This option simply holds the hand in place with stiff impedance control. You can use this mode at the beginning of a constrained manipulation task or to hold position while transitioning between two different constrained manipulation tasks. The target pose to hold will be the measured hand pose upon transitioning to constrained manipulation or upon switching to this task type. This mode should only be used during constrained manipulation tasks, since it uses impedance control to hold the hand in place. This is not intended to stop the arm during position control moves. |
    +
    +
    +
    +
    +
    +### RobotCommandFeedbackStatus.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Behavior execution is in an unknown / unexpected state. |
    +| STATUS_PROCESSING | 1 | The robot is actively working on the command |
    +| STATUS_COMMAND_OVERRIDDEN | 2 | The command was replaced by a new command |
    +| STATUS_COMMAND_TIMED_OUT | 3 | The command expired |
    +| STATUS_ROBOT_FROZEN | 4 | The robot is in an unsafe state, and will only respond to known safe commands. |
    +| STATUS_INCOMPATIBLE_HARDWARE | 5 | The request cannot be executed because the required hardware is missing. / For example, an armless robot receiving a synchronized command with an arm_command / request will return this value in the arm_command_feedback status. |
    +
    +
    +
    +
    +
    +### SE2TrajectoryCommand.Feedback.BodyMovementStatus
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| BODY_STATUS_UNKNOWN | 0 | STATUS_UNKNOWN should never be used. If used, an internal error has happened. |
    +| BODY_STATUS_MOVING | 1 | The robot body is not settled at the goal. |
    +| BODY_STATUS_SETTLED | 2 | The robot is at the goal and the body has stopped moving. |
    +
    +
    +
    +
    +
    +### SE2TrajectoryCommand.Feedback.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | STATUS_UNKNOWN should never be used. If used, an internal error has happened. |
    +| STATUS_AT_GOAL | 1 | The robot has arrived and is standing at the goal. |
    +| STATUS_NEAR_GOAL | 3 | The robot has arrived at the goal and is doing final positioning. |
    +| STATUS_GOING_TO_GOAL | 2 | The robot is attempting to go to a goal. |
    +
    +
    +
    +
    +
    +### SafePowerOffCommand.Feedback.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | STATUS_UNKNOWN should never be used. If used, an internal error has happened. |
    +| STATUS_POWERED_OFF | 1 | Robot has powered off. |
    +| STATUS_IN_PROGRESS | 2 | Robot is trying to safely power off. |
    +
    +
    +
    +
    +
    +### SitCommand.Feedback.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | STATUS_UNKNOWN should never be used. If used, an internal error has happened. |
    +| STATUS_IS_SITTING | 1 | Robot is currently sitting. |
    +| STATUS_IN_PROGRESS | 2 | Robot is trying to sit. |
    +
    +
    +
    +
    +
    +### StanceCommand.Feedback.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 |  |
    +| STATUS_STANCED | 1 | Robot has finished moving feet and they are at the specified position |
    +| STATUS_GOING_TO_STANCE | 2 | Robot is in the process of moving feet to specified position |
    +| STATUS_TOO_FAR_AWAY | 3 | Robot is not moving, the specified stance was too far away. Hint: Try using SE2TrajectoryCommand to safely put the robot at the correct location first. |
    +
    +
    +
    +
    +
    +### StandCommand.Feedback.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | STATUS_UNKNOWN should never be used. If used, an internal error has happened. |
    +| STATUS_IS_STANDING | 1 | Robot has finished standing up and has completed desired body trajectory. Robot is not attempting to move. |
    +| STATUS_IN_PROGRESS | 2 | Robot is trying to come to a steady stand. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# bddf.proto
    +
    +
    +
    +
    +
    +### DataDescriptor
    +
    +A DataDescriptor describes a data block which immediately follows it in the file.
    +A corresponding SeriesDescriptor with a matching series_index must precede this in the file.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| series_index | [uint32](#uint32) | The series_index references the SeriesDescriptor to which the data following is associated. |
    +| timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | The time at which the data is considered to be captured/sampled. E.g., the shutter-close time of a captured image. |
    +| additional_indexes | [int64](#int64) | Sometimes a visualizer will want to organize message by data timestamp, sometimes by the time messages were published or logged. The additional_indexes field allows extra indexes or timestamps to be associated with each data block for this purpose. Other identifying information may also be used here, such as the PID of the process which originated the data (e.g., for detecting if and when that process restarted). The values in this field should correspond to the labels defined in "additional_index_names" in the corresponding SeriesDescriptor. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DescriptorBlock
    +
    +A Descriptor block typically describes a series of messages, but the descriptor at the
    + start of the file describes the contents of the file as a whole, and the descriptor
    + at the end of the file is an index structure to allow efficient access to the contents
    + of the file.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| file_descriptor | [FileFormatDescriptor](#bosdyn.api.FileFormatDescriptor) |  |
    +| series_descriptor | [SeriesDescriptor](#bosdyn.api.SeriesDescriptor) |  |
    +| series_block_index | [SeriesBlockIndex](#bosdyn.api.SeriesBlockIndex) |  |
    +| file_index | [FileIndex](#bosdyn.api.FileIndex) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### FileFormatDescriptor
    +
    +The first block in the file should be a DescriptorBlock containing a FileFormatDescriptor.
    +FileFormatDescriptor indicates the file format version and annotations.
    +Annotations describe things like the robot from which the log was taken and the release id.
    +The format of annotation keys should be
    +  {project-or-organization}/{annotation-name}
    +For example, 'bosdyn/robot-serial-number'.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| version | [FileFormatVersion](#bosdyn.api.FileFormatVersion) | The version number of the BDDF file. |
    +| annotations | [FileFormatDescriptor.AnnotationsEntry](#bosdyn.api.FileFormatDescriptor.AnnotationsEntry) | File/stream-wide annotations to describe the content of the file. |
    +| checksum_type | [FileFormatDescriptor.CheckSumType](#bosdyn.api.FileFormatDescriptor.CheckSumType) | The type of checksum supported by this stream. For BDDF version 1.0.0 this should be SHA1. |
    +| checksum_num_bytes | [uint32](#uint32) | The number of bytes used for the BDDF checksum. For BDDF version 1.0.0 this should always be 20, even if CHECKSUM_NONE is used. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### FileFormatDescriptor.AnnotationsEntry
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| key | [string](#string) |  |
    +| value | [string](#string) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### FileFormatVersion
    +
    +The current data file format is 1.0.0.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| major_version | [uint32](#uint32) |  |
    +| minor_version | [uint32](#uint32) |  |
    +| patch_level | [uint32](#uint32) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### FileIndex
    +
    +As a file is closed, a DescriptorBlock containing a FileIndex should be written.
    +The FileIndex summarizes the data series stored in the file and the location of the
    + block-indexes for each type in the file.
    +Each series is assigned a "series_index" within the file, and this index may be used to
    + index into the repeated fields in this message.
    +E.g., for the series with series_index N, you can access its SeriesIdentifier by accessing
    + element N the of the series_identifiers repeated field.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| series_identifiers | [SeriesIdentifier](#bosdyn.api.SeriesIdentifier) | SeriesIdentifer for each series in this file. |
    +| series_block_index_offsets | [uint64](#uint64) | The offset from the start of the file of the SeriesBlockIndex block for each series. |
    +| series_identifier_hashes | [uint64](#uint64) | The hash of the series_identifier for each series. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### MessageTypeDescriptor
    +
    +If a data series contains a sequence of binary messages, the encoding and format of these
    + messages is described by a MesssageTypeDescriptor.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| content_type | [string](#string) | Description of the content type. E.g., "application/protobuf", "image/jpeg", "text/csv", ... |
    +| type_name | [string](#string) | If content_type is "application/protobuf", this is the full-name of the protobuf type. |
    +| is_metadata | [bool](#bool) | If true, message contents are necessary for interpreting other messages. If the content of this file is split into multiple output files, these messages should be copied into each. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PodTypeDescriptor
    +
    +If a data series contains signals-style data of time-sampled "plain old datatypes", this
    + describes the content of the series.
    +All POD data stored in data blocks is stored in little-endian byte order.
    +Any number of samples may be stored within a given data block.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| pod_type | [PodTypeEnum](#bosdyn.api.PodTypeEnum) | The type of machine-readable values stored. |
    +| dimension | [uint32](#uint32) | If empty, indicates a single POD per sample. If one-element, indicates a vector of the given size per sample. If two-elements, indicates a matrix of the given size, and so on. An M x N x .. x P array of data is traversed from innermost (P) to outermost (M) dimension. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SeriesBlockIndex
    +
    +This describes the location of the SeriesDescriptor DescriptorBlock for the series, and
    + the timestamp and location in the file of every data block in the series.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| series_index | [uint32](#uint32) | The series_index for the series described by this index block. |
    +| descriptor_file_offset | [uint64](#uint64) | Offset of type descriptor block from start of file. |
    +| block_entries | [SeriesBlockIndex.BlockEntry](#bosdyn.api.SeriesBlockIndex.BlockEntry) | The timestamp and location of each data block for this series. |
    +| total_bytes | [uint64](#uint64) | The total size of the data stored in the data blocks of this series. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SeriesBlockIndex.BlockEntry
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | The timestamp of data in this block. |
    +| file_offset | [uint64](#uint64) | The offset of the data block from the start of the file. |
    +| additional_indexes | [int64](#int64) | Values of the additional indexes for describing this block. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SeriesDescriptor
    +
    +A description of a series of data blocks.
    +These data blocks may either represent binary messages of a variable size, or they may
    + represent a sequence of samples of POD data samples: single/vector/matrix/... of integer
    + or floating-point values.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| series_index | [uint32](#uint32) | This index for the series is unique within the data file. |
    +| series_identifier | [SeriesIdentifier](#bosdyn.api.SeriesIdentifier) | This is the globally unique {key -> value} mapping to identify the series. |
    +| identifier_hash | [uint64](#uint64) | This is a hash of the series_identifier. The hash is the first 64 bits (read as a big-endian encoded uint64_t) of SHA1(S K1 V1 K2 V2 ...) where, - S is series identifier text, - K1 and V1 are the key and value of the first key and value of the `spec`, - K2 and V2 are the second key and value of the spec, etc... Here, all strings are encoded as utf-8, and keys are sorted lexicographically using this encoding (K1 < K2 < ...). |
    +| message_type | [MessageTypeDescriptor](#bosdyn.api.MessageTypeDescriptor) |  |
    +| pod_type | [PodTypeDescriptor](#bosdyn.api.PodTypeDescriptor) |  |
    +| struct_type | [StructTypeDescriptor](#bosdyn.api.StructTypeDescriptor) |  |
    +| annotations | [SeriesDescriptor.AnnotationsEntry](#bosdyn.api.SeriesDescriptor.AnnotationsEntry) | Annotations are a {key -> value} mapping for associating additional information with the series. The format of annotation keys should be {project-or-organization}/{annotation-name} For example, 'bosdyn/channel-name', 'bosdyn/protobuf-type'. Annotation keys without a '/' are reserved. The only current key in the reserved namespace is 'units': e.g., {'units': 'm/s2'}. |
    +| additional_index_names | [string](#string) | Labels for additional index values which should be attached to each DataDescriptor in the series. See the description of "additional_indexes" in DataDescriptor. |
    +| description | [string](#string) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SeriesDescriptor.AnnotationsEntry
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| key | [string](#string) |  |
    +| value | [string](#string) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SeriesIdentifier
    +
    +A key or description for selecting a message series.
    +Because there may be multiple ways of describing a message series, we identify
    + them by a unique mapping of {key -> value}.
    +A series_type corresponds to a set of keys which are expected in the mapping.
    +A 'bosdyn:grpc:requests' series_type, containing GRPC robot-id request messages, might
    + thus be specified as:
    +  {'service': 'robot_id', 'message': 'bosdyn.api.RobotIdRequest'}
    +A 'bosdyn:logtick' series_type, containing a signals data variable from LogTick
    +  annotations might be specified as:
    +  {'varname': 'tablet.wifi.rssi', 'schema': 'tablet-comms', 'client': 'bd-tablet'}
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| series_type | [string](#string) | This is the kind of spec, which should correspond to a set of keys which are expected in the spec. |
    +| spec | [SeriesIdentifier.SpecEntry](#bosdyn.api.SeriesIdentifier.SpecEntry) | This is the "key" for naming the series within the file. A key->value description which should be unique for this series within the file with this series_type. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SeriesIdentifier.SpecEntry
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| key | [string](#string) |  |
    +| value | [string](#string) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StructTypeDescriptor
    +
    +A struct series is a composite formed by a set of other series whose messages or signals-ticks
    + are sampled at the same time.
    +For example, all there may be a struct series for a set of signals variables, all from a
    + process with an 'update()' function within which all all variables are sampled with the
    + same timestamp.
    +DataBlocks will not directly reference this series, but only child series of this series.
    +Struct series may reference other struct series, but the series structure must be a directed
    + acyclic graph (DAG): no circular reference structures.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| key_to_series_identifier_hash | [StructTypeDescriptor.KeyToSeriesIdentifierHashEntry](#bosdyn.api.StructTypeDescriptor.KeyToSeriesIdentifierHashEntry) | A map of a name-reference to a series, identified by its series_identifer_hash. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StructTypeDescriptor.KeyToSeriesIdentifierHashEntry
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| key | [string](#string) |  |
    +| value | [uint64](#uint64) |  |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### FileFormatDescriptor.CheckSumType
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| CHECKSUM_TYPE_UNKNOWN | 0 | Checksum type is unspecified. Should not be used. |
    +| CHECKSUM_TYPE_NONE | 1 | The writer of this stream is not computing a checksum. The stream checksum at the end of the file will be 160 bits all set to 0. |
    +| CHECKSUM_TYPE_SHA1 | 2 | A 160 bit SHA1 checksum will be included at the end of the stream. This checksum will be computed over all data before digest itself at the end of the stream, and can be used to verify the stream was received uncorrupted. |
    +
    +
    +
    +
    +
    +### PodTypeEnum
    +
    +"Plain old data" types which may be stored within POD data blocks.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| TYPE_UNSPECIFIED | 0 |  |
    +| TYPE_INT8 | 1 |  |
    +| TYPE_INT16 | 2 |  |
    +| TYPE_INT32 | 3 |  |
    +| TYPE_INT64 | 4 |  |
    +| TYPE_UINT8 | 5 |  |
    +| TYPE_UINT16 | 6 |  |
    +| TYPE_UINT32 | 7 |  |
    +| TYPE_UINT64 | 8 |  |
    +| TYPE_FLOAT32 | 9 |  |
    +| TYPE_FLOAT64 | 10 |  |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# data_acquisition.proto
    +
    +
    +
    +
    +
    +### AcquireDataRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| action_id | [CaptureActionId](#bosdyn.api.CaptureActionId) | Define the unique action that all data should be saved with. |
    +| metadata | [Metadata](#bosdyn.api.Metadata) | Metadata to store with the data capture. The main DAQ service saves it in the DataBuffer. |
    +| acquisition_requests | [AcquisitionRequestList](#bosdyn.api.AcquisitionRequestList) | List of capability requests that should be collected as part of this capture action. |
    +| min_timeout | [google.protobuf.Duration](#google.protobuf.Duration) | Optional duration used to extend the amount of time that the data request may take, in the event that a plugin is incorrectly specifying its timeout. The amount of time allowed will be the maximum of this duration and any requests made to plugins or other capture sources. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### AcquireDataResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header |
    +| status | [AcquireDataResponse.Status](#bosdyn.api.AcquireDataResponse.Status) | Result of the AcquirePluginData RPC call. Further monitoring on the success of the acquisition request can be done using the GetStatus RPC. |
    +| request_id | [uint32](#uint32) | Identifier which can be used to check the status of or cancel the acquisition action.. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### AcquirePluginDataRequest
    +
    +Message sent by main DAQ service to all data acquisition plugin services.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header |
    +| data_id | [DataIdentifier](#bosdyn.api.DataIdentifier) | Metadata acquirers use these DataIdentifier objects to associate them with the acquired metadata when storing them in the DataBuffer. Data acquirers simply get the timestamp from these DataIdentifier objects to use when storing the data in the DataBuffer. |
    +| metadata | [Metadata](#bosdyn.api.Metadata) | Metadata specified by the requestor. |
    +| action_id | [CaptureActionId](#bosdyn.api.CaptureActionId) | Id to be associated with all the data buffered for this request. It will be stored in the DataIdentifier field of each piece of data buffered from this request. |
    +| acquisition_requests | [AcquisitionRequestList](#bosdyn.api.AcquisitionRequestList) | List of capability requests specific for this DAQ plugin. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### AcquirePluginDataResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header |
    +| status | [AcquirePluginDataResponse.Status](#bosdyn.api.AcquirePluginDataResponse.Status) | Result of the AcquirePluginData RPC call. Further monitoring on the success of the acquisition request can be done using the GetStatus RPC. |
    +| request_id | [uint32](#uint32) | Identifier which can be used to check the status of or cancel the acquisition action.. |
    +| timeout_deadline | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Time (in the robot's clock) by which this capture should definitely be complete. If it is not complete by this time, something has gone wrong. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### AcquisitionCapabilityList
    +
    +A list of all capabilities (data and images) that a specific data acquisition plugin service can successfully
    +acquire and save the data specified in each capability.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| data_sources | [DataAcquisitionCapability](#bosdyn.api.DataAcquisitionCapability) | List of non-image data acquisition capabilities. |
    +| image_sources | [ImageAcquisitionCapability](#bosdyn.api.ImageAcquisitionCapability) | List of image data acquisition capabilities. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### AcquisitionRequestList
    +
    +The grouping of all individual image and data captures for a given capture action.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| image_captures | [ImageSourceCapture](#bosdyn.api.ImageSourceCapture) | List of image requests. |
    +| data_captures | [DataCapture](#bosdyn.api.DataCapture) | List of non-image data and metadata requests. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### AssociatedMetadata
    +
    +This message can be stored as a DataBlob in the data buffer in order to be recognized as
    +metadata that is associated with previously stored data.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| reference_id | [DataIdentifier](#bosdyn.api.DataIdentifier) | The data that this metadata refers to. The timestamp field is ignored. If only the action_id is filled out, this metadata is associated with the entire capture action. |
    +| metadata | [Metadata](#bosdyn.api.Metadata) | Metadata message to be stored. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### CancelAcquisitionRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header |
    +| request_id | [uint32](#uint32) | Which acquisition request to cancel. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### CancelAcquisitionResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header |
    +| status | [CancelAcquisitionResponse.Status](#bosdyn.api.CancelAcquisitionResponse.Status) | The status of the Cancellation RPC. Further monitoring on the success of the cancellation request can be done using the GetStatus RPC. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### CaptureActionId
    +
    +The CaptureActionId describes the entire capture action for an AcquireData request and will be used
    +to uniquely identify that full request's set of stored data.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| action_name | [string](#string) | The action name is used to group all pieces of data associated with a single AcquireData request. The action name must be unique for the given group name, meaning no two actions with the same group name can have the same action name. |
    +| group_name | [string](#string) | The group name is used to group a "session" of data, such as a mission or a teleop capture session, which includes multiple capture actions (from multiple AcquireData RPCs). |
    +| timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Time (in the robot's clock) at which this capture was triggered. If the timestamp is not specified in the AcquireData RPC, the main data acquisition service on robot will populate the timestamp field with the timestamp of when the RPC was recieved. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DataAcquisitionCapability
    +
    +Description of a data acquisition capability. A data acquisition plugin service will have a
    +set of capabilities for which it can acquire and save the appropriate data.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| name | [string](#string) | Unique identifier for the data acquisition capability. Used for identification purposes when making acquire data requests. |
    +| description | [string](#string) | A human readable name of the data acquisition capability that will be shown on the tablet. |
    +| channel_name | [string](#string) | Channel name that will be associated with all data stored in the data buffer through each data acquisition plugin. Metadata acquirers do not specify this field. |
    +| service_name | [string](#string) | The data acquisition plugin service's service name used in directory registration. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DataCapture
    +
    +An individual capture which can be specified in the AcquireData request to identify a piece of
    +non-image data to be collected.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| name | [string](#string) | Name of the data to be captured. This should match the uniquely identifying name from the DataAcquisitionCapability. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DataError
    +
    +An error associated with a particular capture action and piece of data.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| data_id | [DataIdentifier](#bosdyn.api.DataIdentifier) | Identifier for the data to be saved. |
    +| error_message | [string](#string) | Human-readable message describing the error. |
    +| error_data | [google.protobuf.Any](#google.protobuf.Any) | Custom plugin-specific data about the problem. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DataIdentifier
    +
    +A way to identify an individual piece of data stored in the data buffer.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| action_id | [CaptureActionId](#bosdyn.api.CaptureActionId) | The action where the data was acquired. |
    +| channel | [string](#string) | Data buffer channel associated with the DataBlob. The channel is used to group data across actions by a specific source, and it can be used in queries to retrieve some subset of data. For example, the channel could be "ptz" and queries can be made for all PTZ images. |
    +| data_name | [string](#string) | Data-specific identifier that can optionally be used to disambiguate cases where the action_id and channel are insufficient. For example, you save cropped SpotCAM pano image that are detected as gauges to a "detected_gauges" channel, but want a way to further individually identify them as each specific gauge, so you give each detection a unique data_name. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetServiceInfoRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetServiceInfoResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| capabilities | [AcquisitionCapabilityList](#bosdyn.api.AcquisitionCapabilityList) | List of capabilities that the data acquisition (plugin) service can collect and save data for. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetStatusRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header |
    +| request_id | [uint32](#uint32) | Which acquisition to check the status of. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetStatusResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header |
    +| status | [GetStatusResponse.Status](#bosdyn.api.GetStatusResponse.Status) |  |
    +| data_saved | [DataIdentifier](#bosdyn.api.DataIdentifier) | Data that has been successfully saved into the data buffer for the capture action. |
    +| data_errors | [DataError](#bosdyn.api.DataError) | A list of data captures which have failed in some way during the action. For example, the data acquisition plugin is unable to communicate to a sensor and responds with a data error for that specific data capture. |
    +| service_errors | [PluginServiceError](#bosdyn.api.PluginServiceError) | Services which failed independent of a particular data id. For example, if a plugin times out or crashes, it could be reported here. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ImageAcquisitionCapability
    +
    +Description of an image acquisition capability. The image acquisition capabilities will be available
    +through the main data acquisition service on robot and are populated based on all bosdyn.api.ImageService
    +services registered to the robot's directory.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| service_name | [string](#string) | The image service's service name used in directory registration. |
    +| image_source_names | [string](#string) | List of the image source names reported by the image service (through the ListImageSources RPC). |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ImageSourceCapture
    +
    +An individual capture which can be specified in the AcquireData request to identify a piece of
    +image data to be collected.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| image_service | [string](#string) | Name of the image service that the data should be requested from. |
    +| image_source | [string](#string) | Specific image source to use from the list reported by the image service within the ImageAcquisitionCapability message. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Metadata
    +
    +Structured data that can be included within a AcquireData RPC and saved in association with
    +that capture action.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| data | [google.protobuf.Struct](#google.protobuf.Struct) | JSON representation of metadata. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PluginServiceError
    +
    +An error associated with a particular data acquisition plugin service that was
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| service_name | [string](#string) | Name of the service with the error |
    +| error | [PluginServiceError.ErrorCode](#bosdyn.api.PluginServiceError.ErrorCode) | Failure mode. |
    +| message | [string](#string) | Description of the error. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### AcquireDataResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 |  |
    +| STATUS_OK | 1 | The capture action has successfully started acquiring the data. |
    +| STATUS_UNKNOWN_CAPTURE_TYPE | 2 | One of the capability requests in the AcquisitionRequestList is unknown. |
    +
    +
    +
    +
    +
    +### AcquirePluginDataResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 |  |
    +| STATUS_OK | 1 | The capture action has successfully started acquiring the data. |
    +| STATUS_UNKNOWN_CAPTURE_TYPE | 2 | One of the capability requests in the AcquisitionRequestList is unknown. |
    +
    +
    +
    +
    +
    +### CancelAcquisitionResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 |  |
    +| STATUS_OK | 1 | Successfully cancelled the data acquisition request. |
    +| STATUS_FAILED_TO_CANCEL | 2 | Unable to stop the data acquisition request. |
    +| STATUS_REQUEST_ID_DOES_NOT_EXIST | 3 | [Error] The request_id does not exist. |
    +
    +
    +
    +
    +
    +### GetStatusResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 |  |
    +| STATUS_ACQUIRING | 1 | [Status] Data acquisition is still in progress |
    +| STATUS_SAVING | 2 | [Status] Data has been acquired, processing and storage is now in progress. |
    +| STATUS_COMPLETE | 3 | [Status] Data acquisition is complete. |
    +| STATUS_CANCEL_IN_PROGRESS | 4 | [Status] The data acquisition service is working to cancel the request. |
    +| STATUS_ACQUISITION_CANCELLED | 5 | [Status] The data acquisition request was cancelled manually. |
    +| STATUS_DATA_ERROR | 10 | [Error - AcquireData] An error occurred while acquiring, processing, or saving data. |
    +| STATUS_TIMEDOUT | 11 | [Error - AcquireData] The data collection has passed the deadline for completion. |
    +| STATUS_INTERNAL_ERROR | 12 | [Error - AcquireData] An error occurred such that we don't even know our status. |
    +| STATUS_CANCEL_ACQUISITION_FAILED | 30 | [Error -CancelAcquisition] The cancellation request failed to complete. |
    +| STATUS_REQUEST_ID_DOES_NOT_EXIST | 20 | [Error - GetStatus] The request_id does not exist. |
    +
    +
    +
    +
    +
    +### PluginServiceError.ErrorCode
    +
    +Possible ways a plugin can fail.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 |  |
    +| STATUS_REQUEST_ERROR | 1 | The initial RPC to the plugin failed |
    +| STATUS_GETSTATUS_ERROR | 2 | The GetStatus request to the plugin failed with a data error or timeout. |
    +| STATUS_INTERNAL_ERROR | 3 | The plugin reported an internal error. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# data_acquisition_plugin_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### DataAcquisitionPluginService
    +
    +The DataAcquisitionPluginService is a gRPC service that a payload developer implements to retrieve
    +data from a sensor (or more generally perform some payload action) and optionally store that data
    +on the robot via the DataAcquisitionStore service.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| AcquirePluginData | [AcquirePluginDataRequest](#bosdyn.api.AcquirePluginDataRequest) | [AcquirePluginDataResponse](#bosdyn.api.AcquirePluginDataResponse) | Trigger a data acquisition to save metadata and non-image data to the data buffer. Sent by the main DAQ as a result of a data acquisition request from the tablet or a client. |
    +| GetStatus | [GetStatusRequest](#bosdyn.api.GetStatusRequest) | [GetStatusResponse](#bosdyn.api.GetStatusResponse) | Query the status of a data acquisition. |
    +| GetServiceInfo | [GetServiceInfoRequest](#bosdyn.api.GetServiceInfoRequest) | [GetServiceInfoResponse](#bosdyn.api.GetServiceInfoResponse) | Get information from a DAQ service; lists acquisition capabilities. |
    +| CancelAcquisition | [CancelAcquisitionRequest](#bosdyn.api.CancelAcquisitionRequest) | [CancelAcquisitionResponse](#bosdyn.api.CancelAcquisitionResponse) | Cancel an in-progress data acquisition. |
    +
    + 
    +
    +
    +
    +
    +
    +# data_acquisition_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### DataAcquisitionService
    +
    +The DataAcquisitionService is the main data acquisition service run on robot, which recieves
    +incoming requests and sends queries to all directory-registered DataAcquisitionPluginServices.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| AcquireData | [AcquireDataRequest](#bosdyn.api.AcquireDataRequest) | [AcquireDataResponse](#bosdyn.api.AcquireDataResponse) | Trigger a data acquisition to save data and metadata to the data buffer. Sent by the tablet or a client to initiate a data acquisition and buffering process. |
    +| GetStatus | [GetStatusRequest](#bosdyn.api.GetStatusRequest) | [GetStatusResponse](#bosdyn.api.GetStatusResponse) | Query the status of a data acquisition. |
    +| GetServiceInfo | [GetServiceInfoRequest](#bosdyn.api.GetServiceInfoRequest) | [GetServiceInfoResponse](#bosdyn.api.GetServiceInfoResponse) | Get information from a DAQ service; lists acquisition capabilities. |
    +| CancelAcquisition | [CancelAcquisitionRequest](#bosdyn.api.CancelAcquisitionRequest) | [CancelAcquisitionResponse](#bosdyn.api.CancelAcquisitionResponse) | Cancel an in-progress data acquisition. |
    +
    + 
    +
    +
    +
    +
    +
    +# data_acquisition_store.proto
    +
    +
    +
    +
    +
    +### ActionIdQuery
    +
    +A query parameter which filters the possible set of data identifiters to those
    +which contain the same action/group names matching any of the names in the
    +set of CaptureActionIds.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| action_ids | [CaptureActionId](#bosdyn.api.CaptureActionId) | The action ids to filter with. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DataQueryParams
    +
    +The message containing the different query parameters which can be applied to
    +the ListData requests.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| time_range | [TimeRangeQuery](#bosdyn.api.TimeRangeQuery) | Time range to query. |
    +| action_ids | [ActionIdQuery](#bosdyn.api.ActionIdQuery) | List of action ids to query. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListCaptureActionsRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| query | [DataQueryParams](#bosdyn.api.DataQueryParams) | Query parameters for finding action ids. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListCaptureActionsResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| action_ids | [CaptureActionId](#bosdyn.api.CaptureActionId) | List of action ids that satisfied the query parameters. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListStoredDataRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| query | [DataQueryParams](#bosdyn.api.DataQueryParams) | Query parameters for finding data. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListStoredDataResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| data_ids | [DataIdentifier](#bosdyn.api.DataIdentifier) | List of data identifiers that satisfied the query parameters. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListStoredImagesRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| query | [DataQueryParams](#bosdyn.api.DataQueryParams) | Query parameters for finding images. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListStoredImagesResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| data_ids | [DataIdentifier](#bosdyn.api.DataIdentifier) | List of image data identifiers that satisfied the query parameters. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListStoredMetadataRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| query | [DataQueryParams](#bosdyn.api.DataQueryParams) | Query parameters for finding metadata. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListStoredMetadataResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| data_ids | [DataIdentifier](#bosdyn.api.DataIdentifier) | List of metadata data identifiers that satisfied the query parameters. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StoreDataRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| data | [bytes](#bytes) | Data to store. |
    +| data_id | [DataIdentifier](#bosdyn.api.DataIdentifier) | Data identifier of the data. |
    +| file_extension | [string](#string) | File extension to use when writing the data to file. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StoreDataResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StoreImageRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| image | [ImageCapture](#bosdyn.api.ImageCapture) | Image to store. |
    +| data_id | [DataIdentifier](#bosdyn.api.DataIdentifier) | Data identifier of the image. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StoreImageResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StoreMetadataRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| metadata | [AssociatedMetadata](#bosdyn.api.AssociatedMetadata) | Metadata to store. |
    +| data_id | [DataIdentifier](#bosdyn.api.DataIdentifier) | Data identifier of the metadata. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StoreMetadataResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### TimeRangeQuery
    +
    +A query parameter which filters the possible set of data identifiers to
    +those with timestamps within the specified range.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| from_timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Start of the time range to query. |
    +| to_timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | End of the time range to query. |
    +
    +
    +
    +
    +
    + 
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# data_acquisition_store_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### DataAcquisitionStoreService
    +
    +The DataAcquisitionStoreService is used to store data (images, data, metadata) on the robot
    +in association with the DataIdentifiers specified by the DataAcquisitionService. Additionally,
    +requests can be made to the DataAcquisitionStoreService to identify different pieces of data or entire
    +capture actions which match query parameters, such as time ranges or action/group names.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| ListCaptureActions | [ListCaptureActionsRequest](#bosdyn.api.ListCaptureActionsRequest) | [ListCaptureActionsResponse](#bosdyn.api.ListCaptureActionsResponse) | List all CaptureActionIds (which identify an entire AcquireData RPC's data captures) that match the query parameters provided in the request. |
    +| ListStoredData | [ListStoredDataRequest](#bosdyn.api.ListStoredDataRequest) | [ListStoredDataResponse](#bosdyn.api.ListStoredDataResponse) | List data identifiers (which identify specific pieces of data from an action) for stored data that satisfy the query parameters in the request. |
    +| StoreData | [StoreDataRequest](#bosdyn.api.StoreDataRequest) | [StoreDataResponse](#bosdyn.api.StoreDataResponse) | Store arbitrary data associated with a DataIdentifier. |
    +| ListStoredImages | [ListStoredImagesRequest](#bosdyn.api.ListStoredImagesRequest) | [ListStoredImagesResponse](#bosdyn.api.ListStoredImagesResponse) | Type-safe to images: list data identifiers (which identify specific images from an action) for stored images that satisfy the query parameters in the request. |
    +| StoreImage | [StoreImageRequest](#bosdyn.api.StoreImageRequest) | [StoreImageResponse](#bosdyn.api.StoreImageResponse) | Type-safe to images: store image data associated with a DataIdentifier. |
    +| ListStoredMetadata | [ListStoredMetadataRequest](#bosdyn.api.ListStoredMetadataRequest) | [ListStoredMetadataResponse](#bosdyn.api.ListStoredMetadataResponse) | Type-safe to JSON metadata: list data identifiers (which identify specific metadata from an action) for stored metadata that satisfy the query parameters in the request. |
    +| StoreMetadata | [StoreMetadataRequest](#bosdyn.api.StoreMetadataRequest) | [StoreMetadataResponse](#bosdyn.api.StoreMetadataResponse) | Type-safe to JSON metadata: store metadata associated with a DataIdentifier. |
    +
    + 
    +
    +
    +
    +
    +
    +# data_buffer.proto
    +
    +
    +
    +
    +
    +### DataBlob
    +
    +Message-style data to add to the log.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Timestamp of data in robot clock time. This is required. |
    +| channel | [string](#string) | A general label for this blob. This is distinct from type_id, which identifies how the blob is to be parsed. In practice, this is often the same as the type_id. |
    +| type_id | [string](#string) | A description of the data's content and its encoding. This is required. This should be sufficient for deciding how to deserialize the data. For example, this could be the full name of a protobuf message type. |
    +| data | [bytes](#bytes) | Raw data. For example, jpeg data or a serialized protobuf. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Event
    +
    +This message contains event data for logging to the public timeline.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| type | [string](#string) | Type of event, typically prefixed with a project or organization, e.g. "bosdyn:startup" |
    +| description | [string](#string) | Event description. This is optional. |
    +| source | [string](#string) | A description of the source of this event. May be the client name. - Not required to be unique. - Disambiguates the source of similar event types. |
    +| id | [string](#string) | Unique identifier to link start and end messages for events with a duration. - Long running events may have separate messages at the start and end, in case the message for the end of the event is lost. - For events without a separate start and end message (in which case both start_time and end time should be specified), the 'id' field should not be set. - This id is not tracked internally by the service. It is only used to consume the event timeline. - To be effective, the id value should be generated randomly by the client. |
    +| start_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Start and end times for the event: - Some events are instantaneous. For these, set start_timestamp and end_timestamp to the same value and send a single message (without an id). - Some events take time. At the onset, send a message with a unique id, the start time, and type. The end message should include all data from the start message, any additional data, and an end time. If you have the end message, you should not need the start message since it is a strict subset. |
    +| end_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) |  |
    +| level | [Event.Level](#bosdyn.api.Event.Level) | The relative importance of the event. |
    +| parameters | [Parameter](#bosdyn.api.Parameter) | Optional set of event parameters. |
    +| log_preserve_hint | [Event.LogPreserveHint](#bosdyn.api.Event.LogPreserveHint) | Optionally request that the robot try to preserve data near this time for a service log. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### OperatorComment
    +
    +An operator comment to be added to the log.
    +These are notes especially intended to mark when logs should be preserved and reviewed
    + to ensure that robot hardware and/or software is working as intended.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| message | [string](#string) | String annotation message to add to the log. |
    +| timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | The timestamp of the annotation. This must be in robot time. If this is not specified, this will default to the time the server received the message. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RecordDataBlobsRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| blob_data | [DataBlob](#bosdyn.api.DataBlob) | The data blobs to be logged. |
    +| sync | [bool](#bool) | When set, the data blob is committed to the log synchronously. The RPC does not return until the data is written. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RecordDataBlobsResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| errors | [RecordDataBlobsResponse.Error](#bosdyn.api.RecordDataBlobsResponse.Error) | Errors which occurred when logging data blobs. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RecordDataBlobsResponse.Error
    +
    +DataBlob recording error.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| type | [RecordDataBlobsResponse.Error.Type](#bosdyn.api.RecordDataBlobsResponse.Error.Type) | The type of error: if it was caused by the client or the service. |
    +| message | [string](#string) | An error message. |
    +| index | [uint32](#uint32) | The index to identify the data being stored. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RecordEventsRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| events | [Event](#bosdyn.api.Event) | The events to be logged. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RecordEventsResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| errors | [RecordEventsResponse.Error](#bosdyn.api.RecordEventsResponse.Error) | Errors which occurred when logging events. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RecordEventsResponse.Error
    +
    +Event recording error.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| type | [RecordEventsResponse.Error.Type](#bosdyn.api.RecordEventsResponse.Error.Type) | The type of error: if it was caused by the client, the service, or something else. |
    +| message | [string](#string) | An error message. |
    +| index | [uint32](#uint32) | The index to identify the data being stored. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RecordOperatorCommentsRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| operator_comments | [OperatorComment](#bosdyn.api.OperatorComment) | The operator comments to be logged. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RecordOperatorCommentsResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| errors | [RecordOperatorCommentsResponse.Error](#bosdyn.api.RecordOperatorCommentsResponse.Error) | Errors which occurred when logging operator comments. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RecordOperatorCommentsResponse.Error
    +
    +Operator comment recording error.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| type | [RecordOperatorCommentsResponse.Error.Type](#bosdyn.api.RecordOperatorCommentsResponse.Error.Type) | The type of error: if it was caused by the client or the service. |
    +| message | [string](#string) | An error message. |
    +| index | [uint32](#uint32) | The index to identify the data being stored. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RecordSignalTicksRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| tick_data | [SignalTick](#bosdyn.api.SignalTick) | The signals data to be logged. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RecordSignalTicksResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| errors | [RecordSignalTicksResponse.Error](#bosdyn.api.RecordSignalTicksResponse.Error) | Errors which occurred when logging signal ticks. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RecordSignalTicksResponse.Error
    +
    +Signal tick recording error.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| type | [RecordSignalTicksResponse.Error.Type](#bosdyn.api.RecordSignalTicksResponse.Error.Type) | The type of error: if it was caused by the client, the service, or something else. |
    +| message | [string](#string) | An error message. |
    +| index | [uint32](#uint32) | The index to identify the data being stored. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RecordTextMessagesRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| text_messages | [TextMessage](#bosdyn.api.TextMessage) | The text messages to be logged. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RecordTextMessagesResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| errors | [RecordTextMessagesResponse.Error](#bosdyn.api.RecordTextMessagesResponse.Error) | Errors which occurred when logging text message data. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RecordTextMessagesResponse.Error
    +
    +Text message recording error.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| type | [RecordTextMessagesResponse.Error.Type](#bosdyn.api.RecordTextMessagesResponse.Error.Type) | The type of error: if it was caused by the client or the service. |
    +| message | [string](#string) | An error message. |
    +| index | [uint32](#uint32) | The index to identify the data being stored. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RegisterSignalSchemaRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request/response header. |
    +| schema | [SignalSchema](#bosdyn.api.SignalSchema) | Defines a schema for interpreting SignalTick data containing packed signals-type data. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RegisterSignalSchemaResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common request/response header. |
    +| schema_id | [uint64](#uint64) | Server returns a unique ID based on the client ID and schema definition. Always greater than zero. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SignalSchema
    +
    +A description of a set of signals-style variables to log together as timestamped samples.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| vars | [SignalSchema.Variable](#bosdyn.api.SignalSchema.Variable) | A SignalTick using this schema contains the values of this ordered list of variables. |
    +| schema_name | [string](#string) | The name of the schema. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SignalSchema.Variable
    +
    +A variable of signals-style data, which will be sampled in time.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| name | [string](#string) | The name of the variable. |
    +| type | [SignalSchema.Variable.Type](#bosdyn.api.SignalSchema.Variable.Type) | The type of the data. |
    +| is_time | [bool](#bool) | Zero or one variable in 'vars' may be specified as a time variable. A time variable must have type TYPE_FLOAT64. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SignalSchemaId
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| schema_id | [uint64](#uint64) | {schema, id} pair |
    +| schema | [SignalSchema](#bosdyn.api.SignalSchema) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SignalTick
    +
    +A timestamped set of signals variable values.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| sequence_id | [int64](#int64) | Successive ticks should have successive sequence_id's. The robot uses this to determine if a tick was somehow lost. |
    +| timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Timestamp at which the variable values were sampled. |
    +| source | [string](#string) | The client name. This may be used to segregate data for the same variables to different parts of the buffer. |
    +| schema_id | [uint64](#uint64) | This specifies the SignalSchema to be used in interpreting the |data| field. This value was returned by the server when the schema was registered. |
    +| encoding | [SignalTick.Encoding](#bosdyn.api.SignalTick.Encoding) | Format describing how the data bytes array is encoded. |
    +| data | [bytes](#bytes) | The encoded data representing a tick of multiple values of signal-styles data. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### TextMessage
    +
    +A text message to add to the log.
    +These could be internal text-log messages from a client for use in debugging, for example.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| message | [string](#string) | String annotation message. |
    +| timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | The timestamp of the annotation. This must be in robot time. If this is not specified, this will default to the time the server received the message. |
    +| source | [string](#string) | The client name. This may be used to segregate data for the same variables to different parts of the buffer. |
    +| level | [TextMessage.Level](#bosdyn.api.TextMessage.Level) | The relative importance of the message. |
    +| tag | [string](#string) | Optional tag to identify from what code/module this message originated from. |
    +| filename | [string](#string) | Optional source file name originating the log message. |
    +| line_number | [int32](#int32) | Optional source file line number originating the log message. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### Event.Level
    +
    +Level, or similarly "visibility," "importance," or "weight" of event.
    + - Higher level events will increase the visibility on the event timeline, relative to other
    +   events.
    + - In general, higher level events should be more consequential with respect to the robot
    +   operation on a per-occurence basis.
    + - Lower level events should be less consequential on a per occurence basis.
    + - Non-critical events may be one of LOW, MEDIUM, or HIGH.  UNSET is logically equivalent to
    +   LOW level.
    + - Critical events may be either mission or system critical.
    + - System-critical is quasi-reserved for internal robot use, and is used to identify events
    +   that directly affect robot status or capability, such as the onset of a critical fault or
    +   start of an enabling capability.
    + - Mission-critical is quasi-reserved client use, and is intended for events that directly
    +   affect the ability of the robot to "do what the user wants," such as the onset of a
    +   service fault or start of an enabling capability.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| LEVEL_UNSET | 0 | Non-critical events |
    +| LEVEL_LOW | 1 |  |
    +| LEVEL_MEDIUM | 2 |  |
    +| LEVEL_HIGH | 3 |  |
    +| LEVEL_MISSION_CRITICAL | 4 | Critical events |
    +| LEVEL_SYSTEM_CRITICAL | 5 |  |
    +
    +
    +
    +
    +
    +### Event.LogPreserveHint
    +
    +LogPreserveHint may encode a hint to the robot's logging system for whether to preserve
    +internal log data near the time of this event.  This could be useful in saving data
    +to be used in a service log to send to Boston Dynamics.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| LOG_PRESERVE_HINT_UNSET | 0 | If this this is unset, it is equivalent to LOG_PRESERVE_HINT_NORMAL. |
    +| LOG_PRESERVE_HINT_NORMAL | 1 | Do not change the robot's default log data preservation behavior in response to this event. |
    +| LOG_PRESERVE_HINT_PRESERVE | 2 | Request that the robot try to preserve data near the time of this event. Log space on the robot is limited, so this does not guarentee that the data will be preserved. |
    +
    +
    +
    +
    +
    +### RecordDataBlobsResponse.Error.Type
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| NONE | 0 |  |
    +| CLIENT_ERROR | 1 |  |
    +| SERVER_ERROR | 2 |  |
    +
    +
    +
    +
    +
    +### RecordEventsResponse.Error.Type
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| NONE | 0 |  |
    +| CLIENT_ERROR | 1 |  |
    +| SERVER_ERROR | 2 |  |
    +
    +
    +
    +
    +
    +### RecordOperatorCommentsResponse.Error.Type
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| NONE | 0 |  |
    +| CLIENT_ERROR | 1 |  |
    +| SERVER_ERROR | 2 |  |
    +
    +
    +
    +
    +
    +### RecordSignalTicksResponse.Error.Type
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| NONE | 0 |  |
    +| CLIENT_ERROR | 1 |  |
    +| SERVER_ERROR | 2 |  |
    +| INVALID_SCHEMA_ID | 3 |  |
    +
    +
    +
    +
    +
    +### RecordTextMessagesResponse.Error.Type
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| NONE | 0 |  |
    +| CLIENT_ERROR | 1 |  |
    +| SERVER_ERROR | 2 |  |
    +
    +
    +
    +
    +
    +### SignalSchema.Variable.Type
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| TYPE_UNKNOWN | 0 |  |
    +| TYPE_INT8 | 1 |  |
    +| TYPE_INT16 | 2 |  |
    +| TYPE_INT32 | 3 |  |
    +| TYPE_INT64 | 4 |  |
    +| TYPE_UINT8 | 5 |  |
    +| TYPE_UINT16 | 6 |  |
    +| TYPE_UINT32 | 7 |  |
    +| TYPE_UINT64 | 8 |  |
    +| TYPE_FLOAT32 | 9 |  |
    +| TYPE_FLOAT64 | 10 |  |
    +
    +
    +
    +
    +
    +### SignalTick.Encoding
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| ENCODING_UNKNOWN | 0 |  |
    +| ENCODING_RAW | 1 | Bytes array is a concatination of little-endian machine representations of the variables from the SignalSchema, in order listed in that schema. |
    +
    +
    +
    +
    +
    +### TextMessage.Level
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| LEVEL_UNKNOWN | 0 | Invalid, do not use. |
    +| LEVEL_DEBUG | 1 | Events likely of interest only in a debugging context. |
    +| LEVEL_INFO | 2 | Informational message during normal operation. |
    +| LEVEL_WARN | 3 | Information about an unexpected but recoverable condition. |
    +| LEVEL_ERROR | 4 | Information about an operation which did not succeed. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# data_buffer_service.proto
    +DataBufferService allows adding information to the robot's log files.
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### DataBufferService
    +
    +This service is a mechanism for adding information to the robot's log files.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| RecordTextMessages | [RecordTextMessagesRequest](#bosdyn.api.RecordTextMessagesRequest) | [RecordTextMessagesResponse](#bosdyn.api.RecordTextMessagesResponse) | Add text messages to the log. |
    +| RecordOperatorComments | [RecordOperatorCommentsRequest](#bosdyn.api.RecordOperatorCommentsRequest) | [RecordOperatorCommentsResponse](#bosdyn.api.RecordOperatorCommentsResponse) | Add a set of operator messages to the log. |
    +| RecordDataBlobs | [RecordDataBlobsRequest](#bosdyn.api.RecordDataBlobsRequest) | [RecordDataBlobsResponse](#bosdyn.api.RecordDataBlobsResponse) | Add message-style data to the log. |
    +| RecordEvents | [RecordEventsRequest](#bosdyn.api.RecordEventsRequest) | [RecordEventsResponse](#bosdyn.api.RecordEventsResponse) | Add event data to the log. |
    +| RegisterSignalSchema | [RegisterSignalSchemaRequest](#bosdyn.api.RegisterSignalSchemaRequest) | [RegisterSignalSchemaResponse](#bosdyn.api.RegisterSignalSchemaResponse) | Register a log tick schema, allowing client to later log tick data. |
    +| RecordSignalTicks | [RecordSignalTicksRequest](#bosdyn.api.RecordSignalTicksRequest) | [RecordSignalTicksResponse](#bosdyn.api.RecordSignalTicksResponse) | Add signal data for registered signal schema to the log. |
    +
    + 
    +
    +
    +
    +
    +
    +# data_chunk.proto
    +
    +
    +
    +
    +
    +### DataChunk
    +
    +Represents a chunk of (possibly serialized) data.
    +Chunks will be concatenated together to produce a datagram.
    +This is to avoid size limit restrictions in grpc implementations.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| total_size | [uint64](#uint64) | The total size in bytes of the datagram that this chunk is a part of. |
    +| data | [bytes](#bytes) | Bytes in this data chunk. Bytes are sent sequentially. |
    +
    +
    +
    +
    +
    + 
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# data_index.proto
    +
    +
    +
    +
    +
    +### BlobPage
    +
    +A set of blob messages of a given channel/msgtype within a given data page.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| spec | [BlobSpec](#bosdyn.api.BlobSpec) |  |
    +| page | [PageInfo](#bosdyn.api.PageInfo) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### BlobPages
    +
    +A set of pages of data which contain specified Blob messages from the data-buffer.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| time_range | [TimeRange](#bosdyn.api.TimeRange) |  |
    +| pages | [BlobPage](#bosdyn.api.BlobPage) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### BlobSpec
    +
    +Specification for selecting of blob messages.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| source | [string](#string) | If set, require the message source to match this. |
    +| message_type | [string](#string) | If set, require the message type to match this value. |
    +| channel | [string](#string) | If set, require the channel to match this value (or channel_glob, if set). |
    +| channel_glob | [string](#string) | Optionally require the channel to match a glob (or channel, if set).. For example, 'gps/*' will match all channels starting with 'gps/'. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DataBufferStatus
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| num_data_buffer_pages | [int64](#int64) |  |
    +| data_buffer_total_bytes | [int64](#int64) |  |
    +| num_comments | [int64](#int64) |  |
    +| num_events | [int64](#int64) |  |
    +| blob_specs | [BlobSpec](#bosdyn.api.BlobSpec) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DataIndex
    +
    +Description of data matching a given DataQuery.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| time_range | [TimeRange](#bosdyn.api.TimeRange) |  |
    +| blobs | [BlobPages](#bosdyn.api.BlobPages) |  |
    +| text_messages | [PagesAndTimestamp](#bosdyn.api.PagesAndTimestamp) |  |
    +| events | [PagesAndTimestamp](#bosdyn.api.PagesAndTimestamp) |  |
    +| comments | [PagesAndTimestamp](#bosdyn.api.PagesAndTimestamp) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DataQuery
    +
    +A query for pages containing the desired data.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| time_range | [TimeRange](#bosdyn.api.TimeRange) | Timespan for data we want to query |
    +| blobs | [BlobSpec](#bosdyn.api.BlobSpec) | Request for pages containing different kinds of data. |
    +| text_messages | [bool](#bool) | return pages of text-messages during the specified timespan |
    +| events | [bool](#bool) | return pages of events |
    +| comments | [bool](#bool) | return pages of operator comments during the specified timespan |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DeleteDataPagesRequest
    +
    +GRPC request to delete pages. Both time_range and page_ids can be set.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) |  |
    +| time_range | [TimeRange](#bosdyn.api.TimeRange) | Delete all pages in this time range |
    +| page_ids | [string](#string) | Delete all pages with matching ids |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DeleteDataPagesResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) |  |
    +| bytes_deleted | [int64](#int64) |  |
    +| status | [DeletePageStatus](#bosdyn.api.DeletePageStatus) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DeletePageStatus
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| page_id | [string](#string) |  |
    +| status | [DeletePageStatus.Status](#bosdyn.api.DeletePageStatus.Status) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### EventSpec
    +
    +Specification for selecting Events.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| source | [string](#string) |  |
    +| type | [string](#string) |  |
    +| level | [google.protobuf.Int32Value](#google.protobuf.Int32Value) |  |
    +| log_preserve_hint | [Event.LogPreserveHint](#bosdyn.api.Event.LogPreserveHint) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### EventsComments
    +
    +Requested Events and/or OperatorComments.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| time_range | [TimeRange](#bosdyn.api.TimeRange) | Timespan for data |
    +| events | [Event](#bosdyn.api.Event) |  |
    +| operator_comments | [OperatorComment](#bosdyn.api.OperatorComment) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### EventsCommentsSpec
    +
    +A request for Events and/or OperatorComments over a given time range.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| time_range | [TimeRange](#bosdyn.api.TimeRange) | Timespan for data we want to query |
    +| events | [EventSpec](#bosdyn.api.EventSpec) | Return events which match the request. |
    +| comments | [bool](#bool) | Return operator comments which match the request. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetDataBufferStatusRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) |  |
    +| get_blob_specs | [bool](#bool) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetDataBufferStatusResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) |  |
    +| data_buffer_status | [DataBufferStatus](#bosdyn.api.DataBufferStatus) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetDataIndexRequest
    +
    +GRPC response with requested data index information.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) |  |
    +| data_query | [DataQuery](#bosdyn.api.DataQuery) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetDataIndexResponse
    +
    +GRPC request for data index information.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) |  |
    +| data_index | [DataIndex](#bosdyn.api.DataIndex) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetDataPagesRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) |  |
    +| time_range | [TimeRange](#bosdyn.api.TimeRange) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetDataPagesResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) |  |
    +| pages | [PageInfo](#bosdyn.api.PageInfo) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetEventsCommentsRequest
    +
    +GRPC request for Events and OperatorComments.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) |  |
    +| event_comment_request | [EventsCommentsSpec](#bosdyn.api.EventsCommentsSpec) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetEventsCommentsResponse
    +
    +GRPC response with requested Events and OperatorComments.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) |  |
    +| events_comments | [EventsComments](#bosdyn.api.EventsComments) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GrpcPages
    +
    +A set of pages of data which contain specied GRPC request and response messages.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| time_range | [TimeRange](#bosdyn.api.TimeRange) |  |
    +| spec | [GrpcSpec](#bosdyn.api.GrpcSpec) |  |
    +| pages | [PageInfo](#bosdyn.api.PageInfo) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GrpcSpec
    +
    +Specification for selecting of GRPC logs.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| service_name | [string](#string) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PageInfo
    +
    +A unit of data storage.
    +This may be a bddf data file.
    +Like a file, this data may be downloaded or deleted all together for example.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| id | [string](#string) | Identifier unique to robot. |
    +| path | [string](#string) | Relative path to file, if file storage. |
    +| source | [string](#string) | Name of service/client which provided the data. |
    +| time_range | [TimeRange](#bosdyn.api.TimeRange) | Time range of the relevant data in the page. |
    +| num_ticks | [int64](#int64) | Number of time samples or blobs. |
    +| total_bytes | [int64](#int64) | Total size of data in the page. |
    +| format | [PageInfo.PageFormat](#bosdyn.api.PageInfo.PageFormat) |  |
    +| compression | [PageInfo.Compression](#bosdyn.api.PageInfo.Compression) |  |
    +| is_open | [bool](#bool) | True if data is still being written into this page, false if page is complete. |
    +| is_downloaded | [bool](#bool) | True if data is marked as having been downloaded. |
    +| deleted_timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | If this exists, the page was deleted from the robot at the specified time. |
    +| download_started_timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | If this exists, download from this page was started at the specified time. |
    +| request_preserve | [bool](#bool) | True if data has been requested to be preserved. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PagesAndTimestamp
    +
    +A set of pages and the associated time range they cover.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| time_range | [TimeRange](#bosdyn.api.TimeRange) |  |
    +| pages | [PageInfo](#bosdyn.api.PageInfo) |  |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### DeletePageStatus.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 |  |
    +| STATUS_DELETED | 1 |  |
    +| STATUS_DELETION_FAILED | 2 |  |
    +| STATUS_NOT_FOUND | 3 |  |
    +
    +
    +
    +
    +
    +### PageInfo.Compression
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| COMPRESSION_UNKNOWN | 0 | Not set -- do not use. |
    +| COMPRESSION_NONE | 1 | Data is not compressed. |
    +| COMPRESSION_GZIP | 2 | Data uses gzip compression. |
    +| COMPRESSION_ZSTD | 3 | Data uses zstd compression. |
    +
    +
    +
    +
    +
    +### PageInfo.PageFormat
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| FORMAT_UNKNOWN | 0 | Unset -- do not use. |
    +| FORMAT_BDDF_FILE | 1 | Data is stored in a .bddf file |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# data_service.proto
    +DataBufferService allows adding information to the robot's log files.
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### DataService
    +
    +The DataService is a mechanism for querying and managing data stored on robot.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| GetDataIndex | [GetDataIndexRequest](#bosdyn.api.GetDataIndexRequest) | [GetDataIndexResponse](#bosdyn.api.GetDataIndexResponse) | Get index of current data matching a given DataQuery. |
    +| GetEventsComments | [GetEventsCommentsRequest](#bosdyn.api.GetEventsCommentsRequest) | [GetEventsCommentsResponse](#bosdyn.api.GetEventsCommentsResponse) | Get events and comments. |
    +| GetDataBufferStatus | [GetDataBufferStatusRequest](#bosdyn.api.GetDataBufferStatusRequest) | [GetDataBufferStatusResponse](#bosdyn.api.GetDataBufferStatusResponse) | Get basic stats on data buffer storage. |
    +| GetDataPages | [GetDataPagesRequest](#bosdyn.api.GetDataPagesRequest) | [GetDataPagesResponse](#bosdyn.api.GetDataPagesResponse) | Get a list pf pages matching a given time range |
    +| DeleteDataPages | [DeleteDataPagesRequest](#bosdyn.api.DeleteDataPagesRequest) | [DeleteDataPagesResponse](#bosdyn.api.DeleteDataPagesResponse) | Delete a list of pages matching a given time range or page ids |
    +
    + 
    +
    +
    +
    +
    +
    +# directory.proto
    +
    +
    +
    +
    +
    +### Endpoint
    +
    +A message containing information that allows a client to identify a
    +given endpoint host using an ip and a port.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| host_ip | [string](#string) | The IP address of the computer hosting this endpoint. |
    +| port | [int32](#int32) | The port number on which the endpoint is provided, between 0 and 65535. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetServiceEntryRequest
    +
    +The GetServiceEntry request message sends the service name to the robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| service_name | [string](#string) | The unique user-friendly name of the service. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetServiceEntryResponse
    +
    +The GetServiceEntry response message returns a ServiceEntry for the desired service name.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response Header. |
    +| status | [GetServiceEntryResponse.Status](#bosdyn.api.GetServiceEntryResponse.Status) | Current status of the request. |
    +| service_entry | [ServiceEntry](#bosdyn.api.ServiceEntry) | The record for the discovered service. Only set if 'status' field == STATUS_OK. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListServiceEntriesRequest
    +
    +The ListServiceEntries request message will ask the robot for all services.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListServiceEntriesResponse
    +
    +The ListServiceEntries response message returns all known services at the time the request
    +was recieved.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| service_entries | [ServiceEntry](#bosdyn.api.ServiceEntry) | The resources managed by the LeaseService. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ServiceEntry
    +
    +A message representing a discoverable service.  By definition, all services
    +discoverable by this system are expected to be grpc "services" provided by
    +some server.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| name | [string](#string) | The unique user-friendly name of this service. |
    +| type | [string](#string) | The type of this service. Usually identifies the underlying implementation. Does not have to be unique among all ServiceEntry objects. |
    +| authority | [string](#string) | Information used to route to the desired Service. Can either be a full address (aService.spot.robot) or just a DNS label that will be automatically converted to an address (aService). |
    +| last_update | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Last update time in robot timebase for this service record. This serves as the time of the last heartbeat to the robot. |
    +| user_token_required | [bool](#bool) | If 'user_token_required' field is true, any requests to this service must contain a user token for the machine. Requests without a user token will result in a 401. Most services will want to require a user_token, but ones like auth_service do not. |
    +| permission_required | [string](#string) | If 'permission_required' field is non-empty, any requests to this service must have the same string in the "per" claim of the user token. |
    +| liveness_timeout_secs | [double](#double) | Number of seconds to wait between heartbeats before assuming service in no longer live If unset (0) liveness checks will be disabled for this service. |
    +| host_payload_guid | [string](#string) | The GUID of the payload that this service was registered from. An empty string represents a service that was registered via a client using standard user credentials or internal to the robot. This value is set automatically based on the user token and cannot be set or updated via the API, so it should not be populated by the client at registration time. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### GetServiceEntryResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | UNKNOWN should never be used. An internal DirectoryService issue has happened if UNKNOWN is set. |
    +| STATUS_OK | 1 | GetService was successful. The service_entry field is filled out. |
    +| STATUS_NONEXISTENT_SERVICE | 2 | GetService failed because the requested service name does not exist. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# directory_registration.proto
    +
    +
    +
    +
    +
    +### RegisterServiceRequest
    +
    +The RegisterService request message sends the service's entry and endpoint to the robot's directory.
    +This Request serves as a heartbeat to the Directory.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| endpoint | [Endpoint](#bosdyn.api.Endpoint) | The endpoint at which this service may be contacted. |
    +| service_entry | [ServiceEntry](#bosdyn.api.ServiceEntry) | The service to create. The name must not match any existing service. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RegisterServiceResponse
    +
    +The RegisterService response message has information of whether the service was registered correctly.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response Header. |
    +| status | [RegisterServiceResponse.Status](#bosdyn.api.RegisterServiceResponse.Status) | Return status for the request. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### UnregisterServiceRequest
    +
    +The UnregisterService request message will unregister a service based on name.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| service_name | [string](#string) | The unique user-friendly name of the service. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### UnregisterServiceResponse
    +
    +The UnregisterService response message has information of whether the service was unregistered.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response Header. |
    +| status | [UnregisterServiceResponse.Status](#bosdyn.api.UnregisterServiceResponse.Status) | Return status for the request. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### UpdateServiceRequest
    +
    +The UpdateService request message will update a service based on name to include the new endpoint and service entry.
    +This Request serves as a heartbeat to the Directory.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| endpoint | [Endpoint](#bosdyn.api.Endpoint) | The endpoint at which this service may be contacted. |
    +| service_entry | [ServiceEntry](#bosdyn.api.ServiceEntry) | New record for service. The name field is used as lookup key. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### UpdateServiceResponse
    +
    +The UpdateService response message has information of whether the service was updated on robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response Header. |
    +| status | [UpdateServiceResponse.Status](#bosdyn.api.UpdateServiceResponse.Status) | Return status for the request. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### RegisterServiceResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | UNKNOWN should never be used. An internal DirectoryRegistrationService issue has happened if UNKNOWN is set. |
    +| STATUS_OK | 1 | Success. The new service record is available. |
    +| STATUS_ALREADY_EXISTS | 2 | RegisterService failed because a service with this name already exists. |
    +
    +
    +
    +
    +
    +### UnregisterServiceResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | UNKNOWN should never be used. An internal DirectoryRegistrationService issue has happened if UNKNOWN is set. |
    +| STATUS_OK | 1 | Success. The service record was deleted. |
    +| STATUS_NONEXISTENT_SERVICE | 2 | The provided service name was not found. |
    +
    +
    +
    +
    +
    +### UpdateServiceResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | UNKNOWN should never be used. An internal DirectoryRegistrationService issue has happened if UNKNOWN is set. |
    +| STATUS_OK | 1 | Success. The new service record is available. |
    +| STATUS_NONEXISTENT_SERVICE | 2 | The provided service name was not found. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# directory_registration_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### DirectoryRegistrationService
    +
    +DirectoryRegistrationService is a private class that lets services be
    +discovered by clients by adding them to a discovery database.  Services
    +can live on robot, payload, or other accessible cloud-based locations.
    +Each service is responsible for registering itself with this service.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| RegisterService | [RegisterServiceRequest](#bosdyn.api.RegisterServiceRequest) | [RegisterServiceResponse](#bosdyn.api.RegisterServiceResponse) | Called by a producer to register as a provider with the application. Returns the record for that provider. Requires unique name and correctly filled out service record in request. |
    +| UnregisterService | [UnregisterServiceRequest](#bosdyn.api.UnregisterServiceRequest) | [UnregisterServiceResponse](#bosdyn.api.UnregisterServiceResponse) | Called by a producer to remove its registration from the DirectoryManager. |
    +| UpdateService | [UpdateServiceRequest](#bosdyn.api.UpdateServiceRequest) | [UpdateServiceResponse](#bosdyn.api.UpdateServiceResponse) | Update the ServiceEntry for a producer on the server. |
    +
    + 
    +
    +
    +
    +
    +
    +# directory_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### DirectoryService
    +
    +DirectoryService lets clients discover which API services are available on a robot.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| GetServiceEntry | [GetServiceEntryRequest](#bosdyn.api.GetServiceEntryRequest) | [GetServiceEntryResponse](#bosdyn.api.GetServiceEntryResponse) | Get information about a specific service. |
    +| ListServiceEntries | [ListServiceEntriesRequest](#bosdyn.api.ListServiceEntriesRequest) | [ListServiceEntriesResponse](#bosdyn.api.ListServiceEntriesResponse) | List all known services at time of call. |
    +
    + 
    +
    +
    +
    +
    +
    +# docking/docking.proto
    +
    +
    +
    +
    +
    +### ConfigRange
    +
    +The configuration of a range of dock ID's
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| id_start | [uint32](#uint32) | Starting ID |
    +| id_end | [uint32](#uint32) | Ending ID |
    +| type | [DockType](#bosdyn.api.docking.DockType) | Type of dock for this range |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DockState
    +
    +Message describing the overall dock state of the robot, including power & comms connections.  \
    +Not tied to any particular DockingCommand ID.  \
    +Note: [*] indicates fields which are only valid if the status is DOCK_STATUS_DOCKED or DOCK_STATUS_DOCKING  \
    +or DOCK_STATUS_UNDOCKING. \
    +Note: [^] indicates fields which are only valid if the status is DOCK_STATUS_DOCKED.  \
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| status | [DockState.DockedStatus](#bosdyn.api.docking.DockState.DockedStatus) | Status of if the robot is on dock |
    +| dock_type | [DockType](#bosdyn.api.docking.DockType) | [*] Type of the dock |
    +| dock_id | [uint32](#uint32) | [*] ID of the dock |
    +| power_status | [DockState.LinkStatus](#bosdyn.api.docking.DockState.LinkStatus) | [^] Status of power detection from the dock |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DockingCommandFeedbackRequest
    +
    +Message to get the status of a previously issued DockingCommand
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| docking_command_id | [uint32](#uint32) | Unique identifier of the command to get feedback for. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DockingCommandFeedbackResponse
    +
    +Response to a DockingCommandFeedbackRequest for a particualar docking command ID
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| lease_use_result | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | Details about how the lease was used (unset if unknown). |
    +| status | [DockingCommandFeedbackResponse.Status](#bosdyn.api.docking.DockingCommandFeedbackResponse.Status) | Current feedback of specified command ID. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DockingCommandRequest
    +
    +Message to command the robot to dock. \
    +Note: If the robot is docked, you can undock the robot by issuing a command with
    +`prep_pose_behavior=PREP_POSE_UNDOCK`. If undocking, `docking_station_id` is not required.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| lease | [bosdyn.api.Lease](#bosdyn.api.Lease) | The Lease to show ownership of the robot. |
    +| docking_station_id | [uint32](#uint32) | ID of docking station to dock at. This is ignored if undocking the robot, the current dock is used. |
    +| clock_identifier | [string](#string) | Identifier provided by the time sync service to verify time sync between robot and client. |
    +| end_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | The timestamp (in robot time) by which a command must finish executing. This is a required field and used to prevent runaway commands. |
    +| prep_pose_behavior | [PrepPoseBehavior](#bosdyn.api.docking.PrepPoseBehavior) | [Optional] Specify the prep pose behavior |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DockingCommandResponse
    +
    +Response to a DockingCommandRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| lease_use_result | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | Details about how the lease was used. |
    +| status | [DockingCommandResponse.Status](#bosdyn.api.docking.DockingCommandResponse.Status) | Result of issued command. |
    +| docking_command_id | [uint32](#uint32) | Unique identifier for the command (if accepted, `status=STATUS_OK`). |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetDockingConfigRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetDockingConfigResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| dock_configs | [ConfigRange](#bosdyn.api.docking.ConfigRange) | A series of `ConfigRange` specifying details for dock ID numbers. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetDockingStateRequest
    +
    +Message to get the overall docking state
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetDockingStateResponse
    +
    +Response of a GetDockingStateRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| dock_state | [DockState](#bosdyn.api.docking.DockState) |  |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### DockState.DockedStatus
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| DOCK_STATUS_UNKNOWN | 0 | Unknown |
    +| DOCK_STATUS_DOCKED | 1 | Robot is detected as on a dock |
    +| DOCK_STATUS_DOCKING | 2 | Robot is currently running a docking command |
    +| DOCK_STATUS_UNDOCKED | 3 | Robot is not detected as on dock |
    +| DOCK_STATUS_UNDOCKING | 4 | Robot is currently running an undocking command |
    +
    +
    +
    +
    +
    +### DockState.LinkStatus
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| LINK_STATUS_UNKNOWN | 0 | Unknown or Not applicable |
    +| LINK_STATUS_DETECTING | 3 | The link status is being detected |
    +| LINK_STATUS_CONNECTED | 1 | The link is detected as connected |
    +| LINK_STATUS_ERROR | 2 | The link could not be detected |
    +
    +
    +
    +
    +
    +### DockType
    +
    +Type of dock
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| DOCK_TYPE_UNKNOWN | 0 | Unknown type of dock |
    +| DOCK_TYPE_CONTACT_PROTOTYPE | 2 | Prototype version SpotDock |
    +| DOCK_TYPE_SPOT_DOCK | 3 | Production version SpotDock |
    +
    +
    +
    +
    +
    +### DockingCommandFeedbackResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Status is not specified. |
    +| STATUS_IN_PROGRESS | 1 | Docking command is executing. |
    +| STATUS_DOCKED | 2 | Docking command succeeded, the robot is docked. |
    +| STATUS_AT_PREP_POSE | 11 | Final success state for `PREP_POSE_ONLY_POSE` or `PREP_POSE_UNDOCK`. |
    +| STATUS_MISALIGNED | 10 | Misaligned was detected between the robot and the dock. The docking command was aborted to save an ending up in an unrecoverable state, please try again. |
    +| STATUS_OLD_DOCKING_COMMAND | 3 | This DockingCommand overridden by new docking command. |
    +| STATUS_ERROR_DOCK_LOST | 4 | ERROR: The sensed dock has been lost and is no longer found. |
    +| STATUS_ERROR_LEASE | 5 | ERROR: Lease rejected. |
    +| STATUS_ERROR_COMMAND_TIMED_OUT | 6 | ERROR: End time has been reached. |
    +| STATUS_ERROR_NO_TIMESYNC | 7 | ERROR: No Timesync with system. |
    +| STATUS_ERROR_TOO_DISTANT | 8 | ERROR: Provided end time too far in the future. |
    +| STATUS_ERROR_NOT_AVAILABLE | 12 | ERROR: The dock is not available for docking. |
    +| STATUS_ERROR_SYSTEM | 9 | ERROR: Internal system error during execution This error cannot be resolved by issuing a new DockingCommand Check the returned message for details |
    +
    +
    +
    +
    +
    +### DockingCommandResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Status is not specified. |
    +| STATUS_OK | 1 | Docking command accepted |
    +| STATUS_ERROR_LEASE | 4 | ERROR: Lease rejected |
    +| STATUS_ERROR_DOCK_NOT_FOUND | 5 | ERROR: Dock fiducial not found. |
    +| STATUS_ERROR_NOT_DOCKED | 6 | ERROR: Trying to undock while not docked |
    +| STATUS_ERROR_GRIPPER_HOLDING_ITEM | 8 | ERROR: Trying to dock when the arm is holding an object. |
    +| STATUS_ERROR_NOT_AVAILABLE | 9 | ERROR: The dock is not available for docking. |
    +| STATUS_ERROR_SYSTEM | 7 | ERROR: Internal system error during execution This error cannot be resolved by issuing a new DockingCommand Check the returned message for details |
    +
    +
    +
    +
    +
    +### PrepPoseBehavior
    +
    +Defines how and whether we use the "pre-docking" pose.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| PREP_POSE_UNKNOWN | 0 | Default behavior, equivalent to PREP_POSE_USE_POSE. |
    +| PREP_POSE_USE_POSE | 1 | Goes to the pre-docking pose before docking. |
    +| PREP_POSE_SKIP_POSE | 2 | Docks before going to the pre-docking pose. |
    +| PREP_POSE_ONLY_POSE | 3 | Goes to the pre-docking pose, and then returns SUCCESS without docking. |
    +| PREP_POSE_UNDOCK | 4 | Use this enum to undock a currently docked robot. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# docking/docking_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### DockingService
    +
    +The DockingService provides an interface to dock and undock the robot from Spot Docks,
    +as well as get feedback on command status, and get the current docked status of the robot.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| DockingCommand | [DockingCommandRequest](#bosdyn.api.docking.DockingCommandRequest) | [DockingCommandResponse](#bosdyn.api.docking.DockingCommandResponse) | Starts a docking command on the robot. |
    +| DockingCommandFeedback | [DockingCommandFeedbackRequest](#bosdyn.api.docking.DockingCommandFeedbackRequest) | [DockingCommandFeedbackResponse](#bosdyn.api.docking.DockingCommandFeedbackResponse) | Check the status of a docking command. |
    +| GetDockingConfig | [GetDockingConfigRequest](#bosdyn.api.docking.GetDockingConfigRequest) | [GetDockingConfigResponse](#bosdyn.api.docking.GetDockingConfigResponse) | Get the configured dock ID ranges. |
    +| GetDockingState | [GetDockingStateRequest](#bosdyn.api.docking.GetDockingStateRequest) | [GetDockingStateResponse](#bosdyn.api.docking.GetDockingStateResponse) | Get the robot's docking state |
    +
    + 
    +
    +
    +
    +
    +
    +# estop.proto
    +
    +
    +
    +
    +
    +### DeregisterEstopEndpointRequest
    +
    +Deregister the specified E-Stop endpoint registration.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header |
    +| target_endpoint | [EstopEndpoint](#bosdyn.api.EstopEndpoint) | The endpoint to deregister. |
    +| target_config_id | [string](#string) | ID of the configuration we are registering against. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DeregisterEstopEndpointResponse
    +
    +Response to E-Stop endpoint  deregistration request.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common resonse header. |
    +| request | [DeregisterEstopEndpointRequest](#bosdyn.api.DeregisterEstopEndpointRequest) | Copy of the initial request. |
    +| status | [DeregisterEstopEndpointResponse.Status](#bosdyn.api.DeregisterEstopEndpointResponse.Status) | Status code for the response. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### EstopCheckInRequest
    +
    +Client request for setting/maintaining an E-Stop system level.
    +After the first CheckIn, must include response to previous challenge.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| endpoint | [EstopEndpoint](#bosdyn.api.EstopEndpoint) | The endpoint making the request. |
    +| challenge | [uint64](#uint64) | Challenge being responded to. Don't set if this is the first EstopCheckInRequest. |
    +| response | [uint64](#uint64) | Response to above challenge. Don't set if this is the first EstopCheckInRequest. |
    +| stop_level | [EstopStopLevel](#bosdyn.api.EstopStopLevel) | Assert this stop level. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### EstopCheckInResponse
    +
    +Server response to EstopCheckInRequest.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| request | [EstopCheckInRequest](#bosdyn.api.EstopCheckInRequest) | Copy of initial request. |
    +| challenge | [uint64](#uint64) | Next challenge to answer. |
    +| status | [EstopCheckInResponse.Status](#bosdyn.api.EstopCheckInResponse.Status) | Status code for the response. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### EstopConfig
    +
    +Configuration of a root / server.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| endpoints | [EstopEndpoint](#bosdyn.api.EstopEndpoint) | EstopEndpoints that are part of this configuration. Unique IDs do not have to be filled out, but can be. |
    +| unique_id | [string](#string) | Unique ID for this configuration. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### EstopEndpoint
    +
    +An  to the robot software-E-Stop system.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| role | [string](#string) | Role of this endpoint. Should be a user-friendly string, e.g. "OCU". |
    +| name | [string](#string) | Name of this endpoint. Specifies a thing to fill the given role, e.g. "patrol-ocu01" |
    +| unique_id | [string](#string) | Unique ID assigned by the server. |
    +| timeout | [google.protobuf.Duration](#google.protobuf.Duration) | Maximum delay between challenge and response for this endpoint prior to soft power off handling. After timeout seconds has passed, the robot will try to get to a safe state prior to disabling motor power. The robot response is equivalent to an ESTOP_LEVEL_SETTLE_THEN_CUT which may involve the robot sitting down in order to prepare for disabling motor power. |
    +| cut_power_timeout | [google.protobuf.Duration](#google.protobuf.Duration) | Optional maximum delay between challenge and response for this endpoint prior to disabling motor power. After cut_power_timeout seconds has passed, motor power will be disconnected immediately regardless of current robot state. If this value is not set robot will default to timeout plus a nominal expected duration to reach a safe state. In practice this is typically 3-4 seconds. The response is equivalent to an ESTOP_LEVEL_CUT. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### EstopEndpointWithStatus
    +
    +EstopEndpoint with some extra status data.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| endpoint | [EstopEndpoint](#bosdyn.api.EstopEndpoint) | The endpoint. |
    +| stop_level | [EstopStopLevel](#bosdyn.api.EstopStopLevel) | Stop level most recently requested by the endpoint. |
    +| time_since_valid_response | [google.protobuf.Duration](#google.protobuf.Duration) | Time since a valid response was provided by the endpoint. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### EstopSystemStatus
    +
    +Status of Estop system.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| endpoints | [EstopEndpointWithStatus](#bosdyn.api.EstopEndpointWithStatus) | Status for all available endpoints. |
    +| stop_level | [EstopStopLevel](#bosdyn.api.EstopStopLevel) | Current stop level for the system. Will be the most-restrictive stop level specified by an endpoint, or a stop level asserted by the system as a whole (e.g. if an endpoint timed out). |
    +| stop_level_details | [string](#string) | Human-readable information on the stop level. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetEstopConfigRequest
    +
    +Get the active EstopConfig.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| target_config_id | [string](#string) | The 'unique_id' of EstopConfig to get. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetEstopConfigResponse
    +
    +Response to EstopConfigRequest.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| request | [GetEstopConfigRequest](#bosdyn.api.GetEstopConfigRequest) | Copy of the request. |
    +| active_config | [EstopConfig](#bosdyn.api.EstopConfig) | The currently active configuration. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetEstopSystemStatusRequest
    +
    +Ask for the current status of the Estop system.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetEstopSystemStatusResponse
    +
    +Respond with the current Estop system status.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [EstopSystemStatus](#bosdyn.api.EstopSystemStatus) | Status of the Estop system. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RegisterEstopEndpointRequest
    +
    +Register an endpoint.
    +EstopEndpoints must be registered before they can send commands or request challenges.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header |
    +| target_endpoint | [EstopEndpoint](#bosdyn.api.EstopEndpoint) | The endpoint to replace. Set the endpoint's unique ID if replacing an active endpoint. |
    +| target_config_id | [string](#string) | ID of the configuration we are registering against. |
    +| new_endpoint | [EstopEndpoint](#bosdyn.api.EstopEndpoint) | The description of the new endpoint. Do not set the unique ID. It will be ignored. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RegisterEstopEndpointResponse
    +
    +Response to registration request.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header |
    +| request | [RegisterEstopEndpointRequest](#bosdyn.api.RegisterEstopEndpointRequest) | Copy of the initial request. |
    +| new_endpoint | [EstopEndpoint](#bosdyn.api.EstopEndpoint) | The resulting endpoint on success. |
    +| status | [RegisterEstopEndpointResponse.Status](#bosdyn.api.RegisterEstopEndpointResponse.Status) | Status code for the response. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetEstopConfigRequest
    +
    +Set a new active EstopConfig.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| config | [EstopConfig](#bosdyn.api.EstopConfig) | New configuration to set. |
    +| target_config_id | [string](#string) | The 'unique_id' of EstopConfig to replace, if replacing one. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetEstopConfigResponse
    +
    +Response to EstopConfigRequest.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| request | [SetEstopConfigRequest](#bosdyn.api.SetEstopConfigRequest) | Copy of the request. |
    +| active_config | [EstopConfig](#bosdyn.api.EstopConfig) | The currently active configuration. |
    +| status | [SetEstopConfigResponse.Status](#bosdyn.api.SetEstopConfigResponse.Status) |  |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### DeregisterEstopEndpointResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | An unknown / unexpected error occurred. |
    +| STATUS_SUCCESS | 1 | Request succeeded. |
    +| STATUS_ENDPOINT_MISMATCH | 2 | Target endpoint did not match. |
    +| STATUS_CONFIG_MISMATCH | 3 | Registered to wrong configuration. |
    +| STATUS_MOTORS_ON | 4 | You cannot deregister an endpoint while the motors are on. |
    +
    +
    +
    +
    +
    +### EstopCheckInResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Unknown error occurred. |
    +| STATUS_OK | 1 | Valid challenge has been returned. |
    +| STATUS_ENDPOINT_UNKNOWN | 2 | The endpoint specified in the request is not registered. |
    +| STATUS_INCORRECT_CHALLENGE_RESPONSE | 5 | The challenge and/or response was incorrect. |
    +
    +
    +
    +
    +
    +### EstopStopLevel
    +
    +The state of the E-Stop system.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| ESTOP_LEVEL_UNKNOWN | 0 | Invalid stop level. |
    +| ESTOP_LEVEL_CUT | 1 | Immediately cut power to the actuators. |
    +| ESTOP_LEVEL_SETTLE_THEN_CUT | 2 | Prepare for loss of actuator power, then cut power. |
    +| ESTOP_LEVEL_NONE | 4 | No-stop level. The endpoint believes the robot is safe to operate. |
    +
    +
    +
    +
    +
    +### RegisterEstopEndpointResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | An unknown / unexpected error occurred. |
    +| STATUS_SUCCESS | 1 | Request succeeded. |
    +| STATUS_ENDPOINT_MISMATCH | 2 | Target endpoint did not match. |
    +| STATUS_CONFIG_MISMATCH | 3 | Registered to wrong configuration. |
    +| STATUS_INVALID_ENDPOINT | 4 | New endpoint was invalid. |
    +
    +
    +
    +
    +
    +### SetEstopConfigResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | An unknown / unexpected error occurred. |
    +| STATUS_SUCCESS | 1 | Request succeeded. |
    +| STATUS_INVALID_ID | 2 | Tried to replace a EstopConfig, but provided bad ID. |
    +| STATUS_MOTORS_ON | 4 | You cannot set a configuration while the motors are on. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# estop_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### EstopService
    +
    +The software robot E-Stop system:
    + 1. Uses challenge-style communication to enforce end user (aka "originators") connection
    +    for Authority to Operate (ATO).
    + 2. Offers the ability to issue a direct denial of  ATO.
    +The EstopService provides a service interface for the robot EStop/Authority to operate the system.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| RegisterEstopEndpoint | [RegisterEstopEndpointRequest](#bosdyn.api.RegisterEstopEndpointRequest) | [RegisterEstopEndpointResponse](#bosdyn.api.RegisterEstopEndpointResponse) | Register an Estop "originator" or "endpoint". This may be a replacement for another active endpoint. |
    +| DeregisterEstopEndpoint | [DeregisterEstopEndpointRequest](#bosdyn.api.DeregisterEstopEndpointRequest) | [DeregisterEstopEndpointResponse](#bosdyn.api.DeregisterEstopEndpointResponse) | Deregister the requested estop endpoint. |
    +| EstopCheckIn | [EstopCheckInRequest](#bosdyn.api.EstopCheckInRequest) | [EstopCheckInResponse](#bosdyn.api.EstopCheckInResponse) | Answer challenge from previous response (unless this is the first call), and request a stop level. |
    +| GetEstopConfig | [GetEstopConfigRequest](#bosdyn.api.GetEstopConfigRequest) | [GetEstopConfigResponse](#bosdyn.api.GetEstopConfigResponse) | Request the current EstopConfig, describing the expected set of endpoints. |
    +| SetEstopConfig | [SetEstopConfigRequest](#bosdyn.api.SetEstopConfigRequest) | [SetEstopConfigResponse](#bosdyn.api.SetEstopConfigResponse) | Set a new active EstopConfig. |
    +| GetEstopSystemStatus | [GetEstopSystemStatusRequest](#bosdyn.api.GetEstopSystemStatusRequest) | [GetEstopSystemStatusResponse](#bosdyn.api.GetEstopSystemStatusResponse) | Ask for the current status of the estop system. |
    +
    + 
    +
    +
    +
    +
    +
    +# fault_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### FaultService
    +
    +The service fault service enables modification of the robot state ServiceFaultState.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| TriggerServiceFault | [TriggerServiceFaultRequest](#bosdyn.api.TriggerServiceFaultRequest) | [TriggerServiceFaultResponse](#bosdyn.api.TriggerServiceFaultResponse) | Sends a ServiceFault to be reporting in robot state. |
    +| ClearServiceFault | [ClearServiceFaultRequest](#bosdyn.api.ClearServiceFaultRequest) | [ClearServiceFaultResponse](#bosdyn.api.ClearServiceFaultResponse) | Clears an active ServiceFault from robot state. |
    +
    + 
    +
    +
    +
    +
    +
    +# full_body_command.proto
    +
    +
    +
    +
    +
    +### FullBodyCommand
    +
    +The robot command message to specify a basic command that requires full control of the entire
    +robot to be completed.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### FullBodyCommand.Feedback
    +
    +The feedback for the fully body command that will provide information on the progress
    +of the robot command.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| stop_feedback | [StopCommand.Feedback](#bosdyn.api.StopCommand.Feedback) | Feedback for the stop command request. |
    +| freeze_feedback | [FreezeCommand.Feedback](#bosdyn.api.FreezeCommand.Feedback) | Feedback for the freeze command request. |
    +| selfright_feedback | [SelfRightCommand.Feedback](#bosdyn.api.SelfRightCommand.Feedback) | Feedback for the self-right command request. |
    +| safe_power_off_feedback | [SafePowerOffCommand.Feedback](#bosdyn.api.SafePowerOffCommand.Feedback) | Feedback for the safe power off command request. |
    +| battery_change_pose_feedback | [BatteryChangePoseCommand.Feedback](#bosdyn.api.BatteryChangePoseCommand.Feedback) | Feedback for the battery change pose command request. |
    +| payload_estimation_feedback | [PayloadEstimationCommand.Feedback](#bosdyn.api.PayloadEstimationCommand.Feedback) | Feedback for the payload estimation command request. |
    +| constrained_manipulation_feedback | [ConstrainedManipulationCommand.Feedback](#bosdyn.api.ConstrainedManipulationCommand.Feedback) | Feedback for the constrained manipulation command request |
    +| status | [RobotCommandFeedbackStatus.Status](#bosdyn.api.RobotCommandFeedbackStatus.Status) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### FullBodyCommand.Request
    +
    +The full body request must be one of the basic command primitives.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| stop_request | [StopCommand.Request](#bosdyn.api.StopCommand.Request) | Command to stop the robot. |
    +| freeze_request | [FreezeCommand.Request](#bosdyn.api.FreezeCommand.Request) | Command to freeze all joints of the robot. |
    +| selfright_request | [SelfRightCommand.Request](#bosdyn.api.SelfRightCommand.Request) | Command to self-right the robot to a ready position. |
    +| safe_power_off_request | [SafePowerOffCommand.Request](#bosdyn.api.SafePowerOffCommand.Request) | Command to safely power off the robot. |
    +| battery_change_pose_request | [BatteryChangePoseCommand.Request](#bosdyn.api.BatteryChangePoseCommand.Request) | Command to put the robot in a position to easily change the battery. |
    +| payload_estimation_request | [PayloadEstimationCommand.Request](#bosdyn.api.PayloadEstimationCommand.Request) | Command to perform payload mass property estimation |
    +| constrained_manipulation_request | [ConstrainedManipulationCommand.Request](#bosdyn.api.ConstrainedManipulationCommand.Request) | Command to perform full body constrained manipulation moves |
    +| params | [google.protobuf.Any](#google.protobuf.Any) | Robot specific command parameters. |
    +
    +
    +
    +
    +
    + 
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# geometry.proto
    +
    +
    +
    +
    +
    +### Area
    +
    +Represents an area in the XY plane.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| polygon | [Polygon](#bosdyn.api.Polygon) |  |
    +| circle | [Circle](#bosdyn.api.Circle) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Bounds
    +
    +Represents bounds on a value, such that lower < value < upper.
    +If you do not want to specify one side of the bound, set it to
    +an appropriately large (or small) number.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| lower | [double](#double) |  |
    +| upper | [double](#double) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Box2
    +
    +Geometric primitive describing a two-dimensional box.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| size | [Vec2](#bosdyn.api.Vec2) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Box2WithFrame
    +
    +Geometric primitive to describe a 2D box in a specific frame.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| box | [Box2](#bosdyn.api.Box2) | The box is specified with width (y) and length (x), and the full box is fixed at an origin, where it's sides are along the coordinate frame's axes. |
    +| frame_name | [string](#string) | The pose of the axis-aligned box is in 'frame_name'. |
    +| frame_name_tform_box | [SE3Pose](#bosdyn.api.SE3Pose) | The transformation of the axis-aligned box into the desired frame (specified above). |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Box3
    +
    +Geometric primitive describing a three-dimensional box.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| size | [Vec3](#bosdyn.api.Vec3) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Box3WithFrame
    +
    +Geometric primitive to describe a 3D box in a specific frame.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| box | [Box3](#bosdyn.api.Box3) | The box width (y), length (x), and height (z) are interpreted in, and the full box is fixed at an origin, where it's sides are along the coordinate frame's axes. |
    +| frame_name | [string](#string) | The pose of the axis-aligned box is in 'frame_name'. |
    +| frame_name_tform_box | [SE3Pose](#bosdyn.api.SE3Pose) | The transformation of the axis-aligned box into the desired frame (specified above). |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Circle
    +
    +Represents a circular 2D area.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| center_pt | [Vec2](#bosdyn.api.Vec2) |  |
    +| radius | [double](#double) | Dimensions in m from center_pt. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### CylindricalCoordinate
    +
    +Cylindrical coordinates are a generalization of polar coordiates, adding a
    +height
    +axis. See (http://mathworld.wolfram.com/CylindricalCoordinates.html) for
    +more details.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| r | [double](#double) | Radial coordinate |
    +| theta | [double](#double) | Azimuthal coordinate |
    +| z | [double](#double) | Vertical coordiante |
    +
    +
    +
    +
    +
    +
    +
    +
    +### FrameTreeSnapshot
    +
    +A frame is a named location in space. \
    +For example, the following frames are defined by the API: \
    + - "body":   A frame centered on the robot's body. \
    + - "vision": A non-moving (inertial) frame that is the robot's best
    +             estimate of a fixed location in the world. It is based on
    +             both dead reckoning and visual analysis of the world. \
    + - "odom":   A non-moving (inertial) frame that is based on the kinematic
    +             odometry of the robot only. \
    +Additional frames are available for robot joints, sensors, and items
    +detected in the world. \
    +
    +The FrameTreeSnapshot represents the relationships between the frames that the robot
    +knows about at a particular point in time. For example, with the FrameTreeSnapshot,
    +an API client can determine where the "body" is relative to the "vision". \
    +
    +To reduce data bandwidth, the FrameTreeSnapshot will typically contain
    +a small subset of all known frames. By default, all services MUST
    +include "vision", "body", and "odom" frames in the FrameTreeSnapshot, but
    +additional frames can also be included. For example, an Image service
    +would likely include the frame located at the base of the camera lens
    +where the picture was taken. \
    +
    +Frame relationships are expressed as edges between "parent" frames and
    +"child" frames, with an SE3Pose indicating the pose of the "child" frame
    +expressed in the "child" frame. These edges are included in the edge_map
    +field. For example, if frame "hand" is 1m in front of the frame "shoulder",
    +then the FrameTreeSnapshot might contain: \
    + edge_map {                                    \
    +    key: "hand"                                \
    +    value: {                                   \
    +        parent_frame_name: "shoulder"          \
    +        parent_tform_child: {                  \
    +           position: {                         \
    +             x: 1.0                            \
    +             y: 0.0                            \
    +             z: 0.0                            \
    +           }                                   \
    +        }                                      \
    +     }                                         \
    + }                                             \
    +
    +Frame relationships can be inverted. So, to find where the "shoulder"
    +is in relationship the "hand", the parent_tform_child pose in the edge
    +above can be inverted: \
    +     hand_tform_shoulder = shoulder_tform_hand.inverse() \
    +Frame relationships can also be concatenated. If there is an additional
    +edge specifying the pose of the "shoulder" relative to the "body", then
    +to find where the "hand" is relative to the "body" do: \
    +     body_tform_hand = body_tform_shoulder * shoulder_tform_hand \
    +
    +The two properties above reduce data size. Instead of having to send N^2
    +edge_map entries to represent all relationships between N frames,
    +only N edge_map entries need to be sent. Clients will need to determine
    +the chain of edges to follow to get from one frame to another frame,
    +and then do inversion and concatentation to generate the appropriate pose. \
    +
    +Note that all FrameTreeSnapshots are expected to be a single rooted tree.
    +The syntax for FrameTreeSnapshot could also support graphs with
    +cycles, or forests of trees - but clients should treat those as invalid
    +representations. \
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| child_to_parent_edge_map | [FrameTreeSnapshot.ChildToParentEdgeMapEntry](#bosdyn.api.FrameTreeSnapshot.ChildToParentEdgeMapEntry) | child_to_parent_edge_map maps the child frame name to the ParentEdge. In aggregate, this forms the tree structure. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### FrameTreeSnapshot.ChildToParentEdgeMapEntry
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| key | [string](#string) |  |
    +| value | [FrameTreeSnapshot.ParentEdge](#bosdyn.api.FrameTreeSnapshot.ParentEdge) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### FrameTreeSnapshot.ParentEdge
    +
    +ParentEdge represents the relationship from a child frame to a parent frame.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| parent_frame_name | [string](#string) | The name of the parent frame. Must be non-empty. If parent_frame_name is not a key in edge_map, it is the root of the tree. |
    +| parent_tform_child | [SE3Pose](#bosdyn.api.SE3Pose) | Transform representing the pose of the child frame in the parent's frame. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Matrix
    +
    +Represents a row-major order matrix of doubles.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| rows | [int32](#int32) |  |
    +| cols | [int32](#int32) |  |
    +| values | [double](#double) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Plane
    +
    +Plane primitive, described with a point and normal.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| point | [Vec3](#bosdyn.api.Vec3) | A point on the plane. |
    +| normal | [Vec3](#bosdyn.api.Vec3) | The direction of the planes normal. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PolyLine
    +
    +Multi-part, 1D line segments defined by a series of points.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| points | [Vec2](#bosdyn.api.Vec2) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Polygon
    +
    +Polygon in the XY plane.
    +May be concave, but should not self-intersect. Vertices can be specified in either
    +clockwise or counterclockwise orders.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| vertexes | [Vec2](#bosdyn.api.Vec2) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PolygonWithExclusions
    +
    +Represents a region in the XY plane that consists of a single polygon
    +from which polygons representing exclusion areas may be subtracted.
    +
    +A point is considered to be inside the region if it is inside the inclusion
    +polygon and not inside any of the exclusion polygons.
    +
    +Note that while this can be used to represent a polygon with holes, that
    +exclusions are not necessarily holes:  An exclusion polygon may not be
    +completely inside the inclusion polygon.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| inclusion | [Polygon](#bosdyn.api.Polygon) |  |
    +| exclusions | [Polygon](#bosdyn.api.Polygon) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Quad
    +
    +A square oriented in 3D space.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| pose | [SE3Pose](#bosdyn.api.SE3Pose) | The center of the quad and the orientation of the normal. The normal axis is [0, 0, 1]. |
    +| size | [double](#double) | The side length of the quad. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Quaternion
    +
    +Quaternion primitive. A quaternion can be used to describe the rotation.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| x | [double](#double) |  |
    +| y | [double](#double) |  |
    +| z | [double](#double) |  |
    +| w | [double](#double) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Ray
    +
    +A ray in 3D space.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| origin | [Vec3](#bosdyn.api.Vec3) | Base of ray. |
    +| direction | [Vec3](#bosdyn.api.Vec3) | Unit vector defining the direction of the ray. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SE2Pose
    +
    +Geometric primitive to describe 2D position and rotation.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| position | [Vec2](#bosdyn.api.Vec2) | (m) |
    +| angle | [double](#double) | (rad) |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SE2Velocity
    +
    +Geometric primitive that describes a 2D velocity through it's linear and angular components.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| linear | [Vec2](#bosdyn.api.Vec2) | (m/s) |
    +| angular | [double](#double) | (rad/s) |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SE2VelocityLimit
    +
    +Geometric primitive to couple minimum and maximum SE2Velocities in a single message.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| max_vel | [SE2Velocity](#bosdyn.api.SE2Velocity) | If set, limits the maximum velocity. |
    +| min_vel | [SE2Velocity](#bosdyn.api.SE2Velocity) | If set, limits the minimum velocity. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SE3Covariance
    +
    +Represents the translation/rotation covariance of an SE3 Pose.
    +The 6x6 matrix can be viewed as the covariance among 6 variables: \
    +     rx     ry  rz    x    y    z                                 \
    +rx rxrx  rxry rxrz  rxx  rxy  rxz                                 \
    +ry ryrx  ryry ryrz  ryx  ryy  ryz                                 \
    +rz rzrx  rzry rzrz  rzx  rzy  rzz                                 \
    +x   xrx   xry  xrz   xx   xy   xz                                 \
    +y   yrx   yry  yrz   yx   yy   yz                                 \
    +z   zrx   zry  zrz   zx   zy   zz                                 \
    +where x, y, z are translations in meters, and rx, ry, rz are rotations around
    +the x, y and z axes in radians.                                   \
    +The matrix is symmetric, so, for example, xy = yx.                \
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| matrix | [Matrix](#bosdyn.api.Matrix) | Row-major order representation of the covariance matrix. |
    +| yaw_variance | [double](#double) | Variance of the yaw component of the SE3 Pose. Warning: deprecated in 2.1. This should equal cov_rzrz, inside `matrix`. |
    +| cov_xx | [double](#double) | Warning: deprecated in 2.1. Use 'matrix.' |
    +| cov_xy | [double](#double) | Warning: deprecated in 2.1. Use 'matrix.' |
    +| cov_xz | [double](#double) | Warning: deprecated in 2.1. Use 'matrix.' |
    +| cov_yx | [double](#double) | Warning: deprecated in 2.1. Use 'matrix.' |
    +| cov_yy | [double](#double) | Warning: deprecated in 2.1. Use 'matrix.' |
    +| cov_yz | [double](#double) | Warning: deprecated in 2.1. Use 'matrix.' |
    +| cov_zx | [double](#double) | Warning: deprecated in 2.1. Use 'matrix.' |
    +| cov_zy | [double](#double) | Warning: deprecated in 2.1. Use 'matrix.' |
    +| cov_zz | [double](#double) | Warning: deprecated in 2.1. Use 'matrix.' |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SE3Pose
    +
    +Geometric primitive to describe 3D position and rotation.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| position | [Vec3](#bosdyn.api.Vec3) | (m) |
    +| rotation | [Quaternion](#bosdyn.api.Quaternion) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SE3Velocity
    +
    +Geometric primitive that describes a 3D velocity through it's linear and angular components.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| linear | [Vec3](#bosdyn.api.Vec3) | (m/s) |
    +| angular | [Vec3](#bosdyn.api.Vec3) | (rad/s) |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Vec2
    +
    +Two dimensional vector primitive.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| x | [double](#double) |  |
    +| y | [double](#double) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Vec2Value
    +
    +A 2D vector of doubles that uses wrapped values so we can tell which elements are set.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| x | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) |  |
    +| y | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Vec3
    +
    +Three dimensional vector primitive.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| x | [double](#double) |  |
    +| y | [double](#double) |  |
    +| z | [double](#double) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Vec3Value
    +
    +A 3D vector of doubles that uses wrapped values so we can tell which elements are set.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| x | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) |  |
    +| y | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) |  |
    +| z | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Volume
    +
    +Represents a volume of space in an unspecified frame.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| box | [Vec3](#bosdyn.api.Vec3) | Dimensions in m, centered on frame origin. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Wrench
    +
    +Geometric primitive used to specify forces and torques.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| force | [Vec3](#bosdyn.api.Vec3) | (N) |
    +| torque | [Vec3](#bosdyn.api.Vec3) | (Nm) |
    +
    +
    +
    +
    +
    + 
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# graph_nav/graph_nav.proto
    +
    +
    +
    +
    +
    +### ClearGraphRequest
    +
    +Clears the graph on the server. Also clears GraphNav's localization to the graph.
    +Note that waypoint and edge snapshots may still be cached on the server after this
    +operation.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| lease | [bosdyn.api.Lease](#bosdyn.api.Lease) | The Lease to show ownership of graph-nav service. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ClearGraphResponse
    +
    +The results of the ClearGraphRequest.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| lease_use_result | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | Details about how the lease was used. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DownloadEdgeSnapshotRequest
    +
    +The DownloadEdgeSnapshot request asks for a specific edge snapshot id to
    +be downloaded. Edge snapshots contain the large sensor data stored in each edge.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| edge_snapshot_id | [string](#string) | ID of the data associated with an edge. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DownloadEdgeSnapshotResponse
    +
    +The DownloadEdgeSnapshot response streams the data of the edge snapshot id
    +currently being downloaded in data chunks no larger than 4MB in size. It is necessary
    +to stream these data to avoid overwhelming gRPC with large http requests.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [DownloadEdgeSnapshotResponse.Status](#bosdyn.api.graph_nav.DownloadEdgeSnapshotResponse.Status) | Return status for the request. |
    +| edge_snapshot_id | [string](#string) | ID of the snapshot associated with an edge. |
    +| chunk | [bosdyn.api.DataChunk](#bosdyn.api.DataChunk) | Chunk of data to download. Responses are sent in sequence until the data chunk is complete. After receiving all chunks, concatenate them into a single byte string. Then, deserialize the byte string into an EdgeSnapshot object. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DownloadGraphRequest
    +
    +The DownloadGraphRequest requests that the server send the graph (waypoints and edges)
    +to the client. Note that the returned Graph message contains only the topological
    +structure of the map, and not any large sensor data. Large sensor data should be downloaded
    +using DownloadWaypointSnapshotRequest and DownloadEdgeSnapshotRequest. Both snapshots and
    +the graph are required to exist on the server for GraphNav to localize and navigate.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DownloadGraphResponse
    +
    +The DownloadGraph response message includes the current graph on the robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common request header. |
    +| graph | [Graph](#bosdyn.api.graph_nav.Graph) | The structure of the graph. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DownloadWaypointSnapshotRequest
    +
    +The DownloadWaypointSnapshot request asks for a specific waypoint snapshot id to
    +be downloaded and has parameters to decrease the amount of data downloaded. After
    +recording a map, first call the DownloadGraph RPC. Then, for each waypoint snapshot id,
    +request the waypoint snapshot from the server using the DownloadWaypointSnapshot RPC.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| waypoint_snapshot_id | [string](#string) | ID of the snapshot associated with a waypoint. |
    +| download_images | [bool](#bool) | If true, download the full images and point clouds from each camera. |
    +| compress_point_cloud | [bool](#bool) | If true, the point cloud will be compressed using the smallest available point cloud encoding. If false, three 32-bit floats will be used per point. |
    +| do_not_download_point_cloud | [bool](#bool) | Skip downloading the point cloud, and only download other data such as images or world objects. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DownloadWaypointSnapshotResponse
    +
    +The DownloadWaypointSnapshot response streams the data of the waypoint snapshot id
    +currently being downloaded in data chunks no larger than 4MB in size. It is necessary
    +to stream these data to avoid overwhelming gRPC with large http requests.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [DownloadWaypointSnapshotResponse.Status](#bosdyn.api.graph_nav.DownloadWaypointSnapshotResponse.Status) | Return status for the request. |
    +| waypoint_snapshot_id | [string](#string) | ID of the snapshot associated with a waypoint. |
    +| chunk | [bosdyn.api.DataChunk](#bosdyn.api.DataChunk) | Chunk of data to download. Responses are sent in sequence until the data chunk is complete. After receiving all chunks, concatenate them into a single byte string. Then, deserialize the byte string into a WaypointSnapshot object. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetLocalizationStateRequest
    +
    +The GetLocalizationState request message requests the current localization state and any other
    +live data from the robot if desired. The localization consists of a waypoint ID and the relative
    +pose of the robot with respect to that waypoint.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| waypoint_id | [string](#string) | Return the localization relative to this waypoint, if specified. |
    +| request_live_point_cloud | [bool](#bool) | If true, request the live edge-segmented point cloud that was used to generate this localization. |
    +| request_live_images | [bool](#bool) | If true, request the live images from realsense cameras at the time of localization. |
    +| request_live_terrain_maps | [bool](#bool) | If true, request the live terrain maps at the time of localization. |
    +| request_live_world_objects | [bool](#bool) | If true, reqeuest the live world objects at the time of localization. |
    +| request_live_robot_state | [bool](#bool) | If true, requests the full live robot state at the time of localization. |
    +| compress_live_point_cloud | [bool](#bool) | If true, the smallest available encoding will be used for the live point cloud data. If false, three 32 bit floats will be used per point in the point cloud. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetLocalizationStateResponse
    +
    +The GetLocalizationState response message returns the current localization and robot state, as well
    +as any requested live data information.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| localization | [Localization](#bosdyn.api.graph_nav.Localization) | Where the robot currently is. If a waypoint_id was specified in the request, this localization will be relative to that waypoint. |
    +| robot_kinematics | [bosdyn.api.KinematicState](#bosdyn.api.KinematicState) | Robot kinematic state at time of localization. |
    +| remote_cloud_status | [RemotePointCloudStatus](#bosdyn.api.graph_nav.RemotePointCloudStatus) | Status of one or more remote point cloud services (such as velodyne). |
    +| live_data | [WaypointSnapshot](#bosdyn.api.graph_nav.WaypointSnapshot) | Contains live data at the time of localization, with elements only filled out if requested. |
    +| lost_detector_state | [LostDetectorState](#bosdyn.api.graph_nav.LostDetectorState) | If the robot drives around without a good localization for a while, eventually it becomes "lost." I.E. it has a localization, but it no longer trusts that the localization it has is accurate. Lost detector state is available through this message. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### LostDetectorState
    +
    +Message describing whether or not graph nav is lost, and if it is lost, how lost it is.
    +If robot is lost, this state can be reset by either:
    +   * Driving to an area where the robot's localization improves.
    +   * Calling SetLocalization RPC.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| is_lost | [bool](#bool) | Whether or not the robot is currently lost. If this is true, graph nav will reject NavigateTo or NavigateRoute RPC's. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### NavigateRouteRequest
    +
    +A NavigateRoute request message specifies a route of waypoints/edges and parameters
    +about how to get there. Like NavigateTo, this command returns immediately upon
    +processing and provides a command_id that the user can use along with a NavigationFeedbackRequest RPC to
    +poll the system for feedback on this command. The RPC does not block until the route is completed.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| leases | [bosdyn.api.Lease](#bosdyn.api.Lease) | The Lease to show ownership of the robot. |
    +| route | [Route](#bosdyn.api.graph_nav.Route) | A route for the robot to follow. |
    +| route_follow_params | [RouteFollowingParams](#bosdyn.api.graph_nav.RouteFollowingParams) | What should the robot do if it is not at the expected point in the route, or the route is blocked. |
    +| travel_params | [TravelParams](#bosdyn.api.graph_nav.TravelParams) | How to travel the route. |
    +| end_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | The timestamp (in robot time) that the navigation command is valid until. |
    +| clock_identifier | [string](#string) | Identifier provided by the time sync service to verify time sync between robot and client. |
    +| destination_waypoint_tform_body_goal | [bosdyn.api.SE2Pose](#bosdyn.api.SE2Pose) | If provided, graph_nav will move the robot to an SE2 pose relative to the final waypoint in the route. Note that the robot will treat this as a simple goto request. It will first arrive at the destination waypoint, and then travel in a straight line from the destination waypoint to the offset goal, attempting to avoid obstacles along the way. |
    +| command_id | [uint32](#uint32) | Unique identifier for the command. If 0, this is a new command, otherwise it is a continuation of an existing command. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### NavigateRouteResponse
    +
    +Response to a NavigateRouteRequest. This is returned immediately after the request is processed. A command_id
    +is provided to specify the ID that the user may use to poll the system for feedback on the NavigateRoute command.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| lease_use_results | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | Details about how the lease was used. |
    +| status | [NavigateRouteResponse.Status](#bosdyn.api.graph_nav.NavigateRouteResponse.Status) | Return status for the request. |
    +| impaired_state | [bosdyn.api.RobotImpairedState](#bosdyn.api.RobotImpairedState) | If the status is ROBOT_IMPAIRED, this is why the robot is impaired. |
    +| command_id | [uint32](#uint32) | Unique identifier for the command, If 0, command was not accepted. |
    +| error_waypoint_ids | [string](#string) | On a relevant error status code, these fields contain the waypoint/edge IDs that caused the error. |
    +| error_edge_ids | [Edge.Id](#bosdyn.api.graph_nav.Edge.Id) | On a relevant error status code (STATUS_INVALID_EDGE), this is populated with the edge ID's that cased the error. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### NavigateToAnchorRequest
    +
    +The NavigateToAnchorRequest can be used to command GraphNav to drive the robot to a specific
    +place in an anchoring. GraphNav will find the waypoint that has the shortest path length from
    +robot's current position but is still close to the goal. GraphNav will plan a path through the
    +map which most efficiently gets the robot to the goal waypoint, and will then travel
    +in a straight line from the destination waypoint to the offset goal, attempting to avoid
    +obstacles along the way.
    +Parameters are provided which influence how GraphNav will generate and follow the path.
    +This RPC returns immediately after the request is processed. It does not block until GraphNav
    +completes the path to the goal waypoint. The user is expected to periodically check the status
    +of the NavigateToAnchor command using the NavigationFeedbackRequest RPC.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| leases | [bosdyn.api.Lease](#bosdyn.api.Lease) | The Leases to show ownership of the robot and the graph. |
    +| seed_tform_goal | [bosdyn.api.SE3Pose](#bosdyn.api.SE3Pose) | The goal, expressed with respect to the seed frame of the current anchoring. The robot will use the z value to find the goal waypoint, but the final z height the robot achieves will depend on the terrain height at the offset from the goal. |
    +| goal_waypoint_rt_seed_ewrt_seed_tolerance | [bosdyn.api.Vec3](#bosdyn.api.Vec3) | These parameters control selection of the goal waypoint. In seed frame, they are the x, y, and z tolerances with respect to the goal pose within which waypoints will be considered. If these values are negative, or too small, reasonable defaults will be used. |
    +| route_params | [RouteGenParams](#bosdyn.api.graph_nav.RouteGenParams) | Preferences on how to pick the route. |
    +| travel_params | [TravelParams](#bosdyn.api.graph_nav.TravelParams) | Parameters that define how to traverse and end the route. |
    +| end_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | The timestamp (in robot time) that the navigation command is valid until. |
    +| clock_identifier | [string](#string) | Identifier provided by the time sync service to verify time sync between robot and client. |
    +| command_id | [uint32](#uint32) | Unique identifier for the command. If 0, this is a new command, otherwise it is a continuation of an existing command. If this is a continuation of an existing command, all parameters will be ignored, and the old parameters will be preserved. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### NavigateToAnchorResponse
    +
    +Response to a NavigateToAnchorRequest. This is returned immediately after the request is
    +processed. A command_id is provided to specify the ID that the user may use to poll the system
    +for feedback on the NavigateTo command.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| lease_use_results | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | Results of using the various leases. |
    +| status | [NavigateToAnchorResponse.Status](#bosdyn.api.graph_nav.NavigateToAnchorResponse.Status) | Return status for the request. |
    +| impaired_state | [bosdyn.api.RobotImpairedState](#bosdyn.api.RobotImpairedState) | If the status is ROBOT_IMPAIRED, this is why the robot is impaired. |
    +| command_id | [uint32](#uint32) | Unique identifier for the command, If 0, command was not accepted. |
    +| error_waypoint_ids | [string](#string) | On a relevant error status code, these fields contain the waypoint/edge IDs that caused the error. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### NavigateToRequest
    +
    +The NavigateToRequest can be used to command GraphNav to drive the robot to a specific waypoint.
    +GraphNav will plan a path through the map which most efficiently gets the robot to the specified goal waypoint.
    +Parameters are provided which influence how GraphNav will generate and follow the path.
    +This RPC returns immediately after the request is processed. It does not block until GraphNav completes the path
    +to the goal waypoint. The user is expected to periodically check the status of the NavigateTo command using
    +the NavigationFeedbackRequest RPC.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| leases | [bosdyn.api.Lease](#bosdyn.api.Lease) | The Leases to show ownership of the robot and the graph. |
    +| destination_waypoint_id | [string](#string) | ID of the waypoint to go to. |
    +| route_params | [RouteGenParams](#bosdyn.api.graph_nav.RouteGenParams) | Preferences on how to pick the route. |
    +| travel_params | [TravelParams](#bosdyn.api.graph_nav.TravelParams) | Parameters that define how to traverse and end the route. |
    +| end_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | The timestamp (in robot time) that the navigation command is valid until. |
    +| clock_identifier | [string](#string) | Identifier provided by the time sync service to verify time sync between robot and client. |
    +| destination_waypoint_tform_body_goal | [bosdyn.api.SE2Pose](#bosdyn.api.SE2Pose) | If provided, graph_nav will move the robot to an SE2 pose relative to the waypoint. Note that the robot will treat this as a simple goto request. It will first arrive at the destination waypoint, and then travel in a straight line from the destination waypoint to the offset goal, attempting to avoid obstacles along the way. |
    +| command_id | [uint32](#uint32) | Unique identifier for the command. If 0, this is a new command, otherwise it is a continuation of an existing command. If this is a continuation of an existing command, all parameters will be ignored, and the old parameters will be preserved. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### NavigateToResponse
    +
    +Response to a NavigateToRequest. This is returned immediately after the request is processed. A command_id
    +is provided to specify the ID that the user may use to poll the system for feedback on the NavigateTo command.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| lease_use_results | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | Results of using the various leases. |
    +| status | [NavigateToResponse.Status](#bosdyn.api.graph_nav.NavigateToResponse.Status) | Return status for the request. |
    +| impaired_state | [bosdyn.api.RobotImpairedState](#bosdyn.api.RobotImpairedState) | If the status is ROBOT_IMPAIRED, this is why the robot is impaired. |
    +| command_id | [uint32](#uint32) | Unique identifier for the command, If 0, command was not accepted. |
    +| error_waypoint_ids | [string](#string) | On a relevant error status code, these fields contain the waypoint/edge IDs that caused the error. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### NavigationFeedbackRequest
    +
    +The NavigationFeedback request message uses the command_id of a navigation request to get
    +the robot's progress and current status for the command. Note that all commands return immediately
    +after they are processed, and the robot will continue to execute the command asynchronously until
    +it times out or completes. New commands override old ones.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| command_id | [uint32](#uint32) | Unique identifier for the command, provided by nav command response. Omit to get feedback on currently executing command. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### NavigationFeedbackResponse
    +
    +The NavigationFeedback response message returns the robot's
    +progress and current status for the command.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [NavigationFeedbackResponse.Status](#bosdyn.api.graph_nav.NavigationFeedbackResponse.Status) | Return status for the request. |
    +| impaired_state | [bosdyn.api.RobotImpairedState](#bosdyn.api.RobotImpairedState) | If the status is ROBOT_IMPAIRED, this is why the robot is impaired. |
    +| remaining_route | [Route](#bosdyn.api.graph_nav.Route) | Remaining part of current route. |
    +| command_id | [uint32](#uint32) | ID of the command this feedback corresponds to. |
    +| last_ko_tform_goal | [bosdyn.api.SE3Pose](#bosdyn.api.SE3Pose) | The most recent transform describing the robot's pose relative to the navigation goal. |
    +| body_movement_status | [bosdyn.api.SE2TrajectoryCommand.Feedback.BodyMovementStatus](#bosdyn.api.SE2TrajectoryCommand.Feedback.BodyMovementStatus) | Indicates whether the robot's body is currently in motion. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RemotePointCloudStatus
    +
    +Message describing the state of a remote point cloud service (such as a velodyne).
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| service_name | [string](#string) | The name of the point cloud service. |
    +| exists_in_directory | [bool](#bool) | Boolean indicating if the point cloud service was registered in the robot's directory with the provided name. |
    +| has_data | [bool](#bool) | Boolean indicating if the point cloud service is currently outputting data. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RouteFollowingParams
    +
    +These parameters are specific to how the robot follows a specified route in NavigateRoute.
    +
    +For each enum in this message, if UNKNOWN is passed in, we default to one of the values
    +(indicated in the comments). Passing UNKNOWN is not considered a programming error.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| new_cmd_behavior | [RouteFollowingParams.StartRouteBehavior](#bosdyn.api.graph_nav.RouteFollowingParams.StartRouteBehavior) |  |
    +| existing_cmd_behavior | [RouteFollowingParams.ResumeBehavior](#bosdyn.api.graph_nav.RouteFollowingParams.ResumeBehavior) |  |
    +| route_blocked_behavior | [RouteFollowingParams.RouteBlockedBehavior](#bosdyn.api.graph_nav.RouteFollowingParams.RouteBlockedBehavior) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RouteGenParams
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetLocalizationRequest
    +
    +The SetLocalization request is used to initialize or reset the localization of GraphNav
    +to a map. A localization consists of a waypoint ID, and a pose of the robot relative to that waypoint.
    +GraphNav uses the localization to decide how to navigate through a map.
    +The SetLocalizationRequest contains parameters to help find a correct localization. For example,
    +AprilTags (fiducials) may be used to set the localization, or the caller can provide an explicit
    +guess of the localization.
    +Once the SetLocalizationRequest completes, the current localization to the map
    +will be modified, and can be retrieved using a GetLocalizationStateRequest.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| initial_guess | [Localization](#bosdyn.api.graph_nav.Localization) | Operator-supplied guess at localization. |
    +| ko_tform_body | [bosdyn.api.SE3Pose](#bosdyn.api.SE3Pose) | Robot pose when the initial_guess was made. This overcomes the race that occurs when the client is trying to initialize a moving robot. GraphNav will use its local ko_tform_body and this ko_tform_body to update the initial localization guess, if necessary. |
    +| max_distance | [double](#double) | The max distance [meters] is how far away the robot is allowed to localize from the position supplied in the initial guess. If not specified, the offset is used directly. Otherwise it searches a neighborhood of the given size. |
    +| max_yaw | [double](#double) | The max yaw [radians] is how different the localized yaw is allowed to be from the supplied yaw in the initial guess. If not specified, the offset is used directly. Otherwise it searches a neighborhood of the given size. |
    +| fiducial_init | [SetLocalizationRequest.FiducialInit](#bosdyn.api.graph_nav.SetLocalizationRequest.FiducialInit) | Tells the initializer whether to use fiducials, and how to use them. |
    +| use_fiducial_id | [int32](#int32) | If using FIDUCIAL_INIT_SPECIFIC, this is the specific fiducial ID to use for initialization. If no detection of this fiducial exists, the service will return STATUS_NO_MATCHING_FIDUCIAL. If detections exist, but are low quality, STATUS_FIDUCIAL_TOO_FAR_AWAY, FIDUCIAL_TOO_OLD, or FIDUCIAL_POSE_UNCERTAIN will be returned. |
    +| refine_fiducial_result_with_icp | [bool](#bool) | If true, and we are using fiducials during initialization, will run ICP after the fiducial was used for an initial guess. |
    +| do_ambiguity_check | [bool](#bool) | If true, consider how nearby localizations appear (like turned 180). |
    +| restrict_fiducial_detections_to_target_waypoint | [bool](#bool) | If using FIDUCIAL_INIT_SPECIFIC and this is true, the initializer will only consider fiducial detections from the target waypoint (from initial_guess). Otherwise, if the target waypoint does not contain a good measurement of the desired fiducial, nearby waypoints may be used to infer the robot's location. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetLocalizationResponse
    +
    +The SetLocalization response message contains the resulting localization to the map.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| lease_use_result | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | Result of using the lease. |
    +| status | [SetLocalizationResponse.Status](#bosdyn.api.graph_nav.SetLocalizationResponse.Status) | Return status for the request. |
    +| error_report | [string](#string) | If set, describes the reason the status is not OK. |
    +| localization | [Localization](#bosdyn.api.graph_nav.Localization) | Result of localization. |
    +| suspected_ambiguity | [SetLocalizationResponse.SuspectedAmbiguity](#bosdyn.api.graph_nav.SetLocalizationResponse.SuspectedAmbiguity) | Alternative information if the localization is ambiguous. |
    +| impaired_state | [bosdyn.api.RobotImpairedState](#bosdyn.api.RobotImpairedState) | If the status is ROBOT_IMPAIRED, this is why the robot is impaired. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetLocalizationResponse.SuspectedAmbiguity
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| alternate_robot_tform_waypoint | [bosdyn.api.SE3Pose](#bosdyn.api.SE3Pose) | Example of a potentially ambiguous localization near the result of the initialization. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### TravelParams
    +
    +Parameters describing how to travel along a route.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| max_distance | [double](#double) | Threshold for the maximum distance [meters] that defines when we have reached the final waypoint. |
    +| max_yaw | [double](#double) | Threshold for the maximum yaw [radians] that defines when we have reached the final waypoint (ignored if ignore_final_yaw is set to true). |
    +| velocity_limit | [bosdyn.api.SE2VelocityLimit](#bosdyn.api.SE2VelocityLimit) | Speed the robot should use. Omit to let the robot choose. |
    +| ignore_final_yaw | [bool](#bool) | If true, the robot will only try to achieve the final translation of the route. Otherwise, it will attempt to achieve the yaw as well. |
    +| feature_quality_tolerance | [TravelParams.FeatureQualityTolerance](#bosdyn.api.graph_nav.TravelParams.FeatureQualityTolerance) |  |
    +| disable_directed_exploration | [bool](#bool) | Disable directed exploration to skip blocked portions of route |
    +| disable_alternate_route_finding | [bool](#bool) | Disable alternate-route-finding; overrides the per-edge setting in the map. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### UploadEdgeSnapshotRequest
    +
    +Used to upload edge data in chunks for a specific edge snapshot. Edge snapshots contain
    +large sensor data associated with each edge.
    +Chunks will be streamed one at a time to the server. Chunk streaming is required to prevent
    +overwhelming gRPC with large http requests.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common response header. |
    +| chunk | [bosdyn.api.DataChunk](#bosdyn.api.DataChunk) | Serialized bytes of a EdgeSnapshot message, restricted to a chunk no larger than 4MB in size. To break the data into chunks, first serialize it to bytes. Then, send the bytes in order as DataChunk objects. The chunks will be concatenated together on the server, and deserialized |
    +| lease | [bosdyn.api.Lease](#bosdyn.api.Lease) | The Leases to show ownership of the graph-nav service. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### UploadEdgeSnapshotResponse
    +
    +One response for the entire EdgeSnapshot after all chunks have
    +been concatenated and deserialized.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| lease_use_result | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | Details about how the lease was used. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### UploadGraphRequest
    +
    +Uploads a graph to the server. This graph will be appended to the graph that
    +currently exists on the server.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| graph | [Graph](#bosdyn.api.graph_nav.Graph) | Structure of the graph containing waypoints and edges without underlying sensor data. |
    +| lease | [bosdyn.api.Lease](#bosdyn.api.Lease) | The Lease to show ownership of graph-nav service. |
    +| generate_new_anchoring | [bool](#bool) | If this is true, generate an (overwrite the) anchoring on upload. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### UploadGraphResponse
    +
    +Response to the UploadGraphRequest. After uploading a graph, the user is expected
    +to upload large data at waypoints and edges (called snapshots). The response provides
    +a list of snapshot IDs which are not yet cached on the server. Snapshots with these IDs should
    +be uploaded by the client.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [UploadGraphResponse.Status](#bosdyn.api.graph_nav.UploadGraphResponse.Status) | Status for an upload request. |
    +| lease_use_result | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | Details about how the lease was used. |
    +| loaded_waypoint_snapshot_ids | [string](#string) | The waypoint snapshot ids for which there was cached data. |
    +| unknown_waypoint_snapshot_ids | [string](#string) | The waypoint snapshot ids for which there is no cached data. |
    +| loaded_edge_snapshot_ids | [string](#string) | The edge snapshot ids for which there was cached data. |
    +| unknown_edge_snapshot_ids | [string](#string) | The edge snapshot ids for which there was no cached data. |
    +| license_status | [bosdyn.api.LicenseInfo.Status](#bosdyn.api.LicenseInfo.Status) | Large graphs can only be uploaded if the license permits them. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### UploadWaypointSnapshotRequest
    +
    +Used to upload waypoint snapshot in chunks for a specific waypoint snapshot. Waypoint
    +snapshots consist of the large sensor data at each waypoint.
    +Chunks will be streamed one at a time to the server. Chunk streaming is required to prevent
    +overwhelming gRPC with large http requests.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common response header. |
    +| chunk | [bosdyn.api.DataChunk](#bosdyn.api.DataChunk) | Serialized bytes of a WaypointSnapshot message, restricted to a chunk no larger than 4MB in size. To break the data into chunks, first serialize it to bytes. Then, send the bytes in order as DataChunk objects. The chunks will be concatenated together on the server, and deserialized. |
    +| lease | [bosdyn.api.Lease](#bosdyn.api.Lease) | The Leases to show ownership of the graph-nav service. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### UploadWaypointSnapshotResponse
    +
    +One response for the entire WaypointSnapshot after all chunks have
    +been concatenated and deserialized.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| lease_use_result | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | Details about how the lease was used. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### DownloadEdgeSnapshotResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 |  |
    +| STATUS_OK | 1 |  |
    +| STATUS_SNAPSHOT_DOES_NOT_EXIST | 2 | Error where the given snapshot ID does not exist. |
    +
    +
    +
    +
    +
    +### DownloadWaypointSnapshotResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 |  |
    +| STATUS_OK | 1 |  |
    +| STATUS_SNAPSHOT_DOES_NOT_EXIST | 2 | Error where the given snapshot ID does not exist. |
    +
    +
    +
    +
    +
    +### NavigateRouteResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | An unknown / unexpected error occurred. |
    +| STATUS_OK | 1 | Request was accepted. |
    +| STATUS_NO_TIMESYNC | 2 | [Time Error] Client has not done timesync with robot. |
    +| STATUS_EXPIRED | 3 | [Time Error] The command was received after its end time had already passed. |
    +| STATUS_TOO_DISTANT | 4 | [Time Error] The command end time was too far in the future. |
    +| STATUS_ROBOT_IMPAIRED | 5 | [Robot State Error] Cannot navigate a route if the robot has a crtical perception fault, or behavior fault, or LIDAR not working. |
    +| STATUS_RECORDING | 6 | [Robot State Error] Cannot navigate a route while recording a map. |
    +| STATUS_UNKNOWN_ROUTE_ELEMENTS | 8 | [Route Error] One or more waypoints/edges are not in the map. |
    +| STATUS_INVALID_EDGE | 9 | [Route Error] One or more edges do not connect to expected waypoints. |
    +| STATUS_NO_PATH | 20 | [Route Error] There is no path to the specified route. |
    +| STATUS_CONSTRAINT_FAULT | 11 | [Route Error] Route contained a constraint fault. |
    +| STATUS_FEATURE_DESERT | 13 | [Route Error] Route contained too many waypoints with low-quality features. |
    +| STATUS_LOST | 14 | [Route Error] Happens when you try to issue a navigate route while the robot is lost. |
    +| STATUS_NOT_LOCALIZED_TO_ROUTE | 16 | [Route Error] Happens when the current localization doesn't refer to any waypoint in the route (possibly uninitialized localization). |
    +| STATUS_NOT_LOCALIZED_TO_MAP | 19 | [Route Error] Happens when the current localization doesn't refer to any waypoint in the map (possibly uninitialized localization). |
    +| STATUS_COULD_NOT_UPDATE_ROUTE | 15 | [Wrestling Errors] Happens when graph nav refuses to follow the route you specified. Try saying please? |
    +| STATUS_STUCK | 17 | [Route Error] Happens when you try to issue a navigate to while the robot is stuck. Navigate a different route, or clear the route and try again. |
    +| STATUS_UNRECOGNIZED_COMMAND | 18 | [Request Error] Happens when you try to continue a command that was either expired, or had an unrecognized id. |
    +
    +
    +
    +
    +
    +### NavigateToAnchorResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | An unknown / unexpected error occurred. |
    +| STATUS_OK | 1 | Request was accepted. |
    +| STATUS_NO_TIMESYNC | 2 | [Time error] Client has not done timesync with robot. |
    +| STATUS_EXPIRED | 3 | [Time error] The command was received after its end time had already passed. |
    +| STATUS_TOO_DISTANT | 4 | [Time error]The command end time was too far in the future. |
    +| STATUS_ROBOT_IMPAIRED | 5 | [Robot State Error] Cannot navigate a route if the robot has a critical perception fault, or behavior fault, or LIDAR not working. |
    +| STATUS_RECORDING | 6 | [Robot State Error] Cannot navigate a route while recording a map. |
    +| STATUS_NO_ANCHORING | 7 | [Route Error] There is no anchoring. |
    +| STATUS_NO_PATH | 8 | [Route Error] There is no path to a waypoint near the specified goal. If any waypoints were found (but no path), the error_waypoint_ids field will be filled. |
    +| STATUS_FEATURE_DESERT | 10 | [Route Error] Route contained too many waypoints with low-quality features. |
    +| STATUS_LOST | 11 | [Route Error] Happens when you try to issue a navigate to while the robot is lost. |
    +| STATUS_NOT_LOCALIZED_TO_MAP | 13 | [Route Error] Happens when the current localization doesn't refer to any waypoint in the map (possibly uninitialized localization). |
    +| STATUS_COULD_NOT_UPDATE_ROUTE | 12 | [Wrestling error] Happens when graph nav refuses to follow the route you specified. |
    +| STATUS_STUCK | 14 | [Route Error] Happens when you try to issue a navigate to while the robot is stuck. Navigate to a different waypoint, or clear the route and try again. |
    +| STATUS_UNRECOGNIZED_COMMAND | 15 | [Request Error] Happens when you try to continue a command that was either expired, or had an unrecognized id. |
    +| STATUS_INVALID_POSE | 16 | [Route Error] The pose is invalid, or known to be unachievable (upside-down, etc). |
    +
    +
    +
    +
    +
    +### NavigateToResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | An unknown / unexpected error occurred. |
    +| STATUS_OK | 1 | Request was accepted. |
    +| STATUS_NO_TIMESYNC | 2 | [Time error] Client has not done timesync with robot. |
    +| STATUS_EXPIRED | 3 | [Time error] The command was received after its end time had already passed. |
    +| STATUS_TOO_DISTANT | 4 | [Time error]The command end time was too far in the future. |
    +| STATUS_ROBOT_IMPAIRED | 5 | [Robot State Error] Cannot navigate a route if the robot has a critical perception fault, or behavior fault, or LIDAR not working. |
    +| STATUS_RECORDING | 6 | [Robot State Error] Cannot navigate a route while recording a map. |
    +| STATUS_UNKNOWN_WAYPOINT | 7 | [Route Error] One or more of the waypoints specified weren't in the map. |
    +| STATUS_NO_PATH | 8 | [Route Error] There is no path to the specified waypoint. |
    +| STATUS_FEATURE_DESERT | 10 | [Route Error] Route contained too many waypoints with low-quality features. |
    +| STATUS_LOST | 11 | [Route Error] Happens when you try to issue a navigate to while the robot is lost. |
    +| STATUS_NOT_LOCALIZED_TO_MAP | 13 | [Route Error] Happens when the current localization doesn't refer to any waypoint in the map (possibly uninitialized localization). |
    +| STATUS_COULD_NOT_UPDATE_ROUTE | 12 | [Wrestling error] Happens when graph nav refuses to follow the route you specified. |
    +| STATUS_STUCK | 14 | [Route Error] Happens when you try to issue a navigate to while the robot is stuck. Navigate to a different waypoint, or clear the route and try again. |
    +| STATUS_UNRECOGNIZED_COMMAND | 15 | [Request Error] Happens when you try to continue a command that was either expired, or had an unrecognized id. |
    +
    +
    +
    +
    +
    +### NavigationFeedbackResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | An unknown / unexpected error occurred. |
    +| STATUS_FOLLOWING_ROUTE | 1 | The robot is currently, successfully following the route. |
    +| STATUS_REACHED_GOAL | 2 | The robot has reached the final goal of the navigation request. |
    +| STATUS_NO_ROUTE | 3 | There's no route currently being navigated. This can happen if no command has been issued, or if the graph has been changed during navigation. |
    +| STATUS_NO_LOCALIZATION | 4 | Robot is not localized to a route. |
    +| STATUS_LOST | 5 | Robot appears to be lost. |
    +| STATUS_STUCK | 6 | Robot appears stuck against an obstacle. |
    +| STATUS_COMMAND_TIMED_OUT | 7 | The command expired. |
    +| STATUS_ROBOT_IMPAIRED | 8 | Cannot navigate a route if the robot has a crtical perception fault, or behavior fault, or LIDAR not working. See impared_status for details. |
    +| STATUS_CONSTRAINT_FAULT | 11 | The route constraints were not feasible. |
    +| STATUS_COMMAND_OVERRIDDEN | 12 | The command was replaced by a new command |
    +| STATUS_NOT_LOCALIZED_TO_ROUTE | 13 | The localization or route changed mid-traverse. |
    +| STATUS_LEASE_ERROR | 14 | The lease is no longer valid. |
    +
    +
    +
    +
    +
    +### RouteFollowingParams.ResumeBehavior
    +
    +This setting applies when a NavigateRoute command is issued with the same route and
    +final-waypoint-offset. It is not necessary that command_id indicate the same command.
    +The expected waypoint is the last waypoint that GraphNav was autonomously navigating to.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| RESUME_UNKNOWN | 0 | The mode is unset. |
    +| RESUME_RETURN_TO_UNFINISHED_ROUTE | 1 | The robot will find the shortest path to any point on the route after the furthest-along traversed edge, and go to the point that gives that shortest path. Then, the robot will follow the rest of the route from that point. This is the default. |
    +| RESUME_FAIL_WHEN_NOT_ON_ROUTE | 2 | The robot will fail the command with status STATUS_NOT_LOCALIZED_TO_ROUTE. |
    +
    +
    +
    +
    +
    +### RouteFollowingParams.RouteBlockedBehavior
    +
    +This setting applies when the robot discovers that the route is blocked.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| ROUTE_BLOCKED_UNKNOWN | 0 | The mode is unset. |
    +| ROUTE_BLOCKED_REROUTE | 1 | The robot will find the shortest path to any point after the furthest-along blockage, and after the furthest-along traversed edge, and go to the point that gives that shortest path. Then, the robot will follow the rest of the route from that point. If multiple points on the route are similarly close to the robot, the robot will prefer the earliest on the route. This is the default. |
    +| ROUTE_BLOCKED_FAIL | 2 | The robot will fail the command with status STATUS_STUCK; |
    +
    +
    +
    +
    +
    +### RouteFollowingParams.StartRouteBehavior
    +
    +This setting applies when a new NavigateRoute command is issued (different route or
    +final-waypoint-offset), and command_id indicates a new command.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| START_UNKNOWN | 0 | The mode is unset. |
    +| START_GOTO_START | 1 | The robot will find the shortest path to the start of the route, possibly using edges that are not in the route. After going to the start, the robot will follow the route. |
    +| START_GOTO_ROUTE | 2 | The robot will find the shortest path to any point on the route, and go to the point that gives that shortest path. Then, the robot will follow the rest of the route from that point. If multiple points on the route are similarly close to the robot, the robot will prefer the earliest on the route. This is the default. |
    +| START_FAIL_WHEN_NOT_ON_ROUTE | 3 | The robot will fail the command with status STATUS_NOT_LOCALIZED_TO_ROUTE. |
    +
    +
    +
    +
    +
    +### SetLocalizationRequest.FiducialInit
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| FIDUCIAL_INIT_UNKNOWN | 0 | It is a programming error to use this one. |
    +| FIDUCIAL_INIT_NO_FIDUCIAL | 1 | Ignore fiducials during initialization. |
    +| FIDUCIAL_INIT_NEAREST | 2 | Localize to the nearest fiducial in any waypoint. |
    +| FIDUCIAL_INIT_NEAREST_AT_TARGET | 3 | Localize to nearest fiducial at the target waypoint (from initial_guess). |
    +| FIDUCIAL_INIT_SPECIFIC | 4 | Localize to the given fiducial at the target waypoint (from initial_guess) if it exists, or any waypoint otherwise. |
    +
    +
    +
    +
    +
    +### SetLocalizationResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | The status is unknown/unset. |
    +| STATUS_OK | 1 | Localization success. |
    +| STATUS_ROBOT_IMPAIRED | 2 | Robot is experiencing a condition that prevents localization. |
    +| STATUS_UNKNOWN_WAYPOINT | 3 | The given waypoint is unknown by the system. This could be due to a client error, or because the graph was changed out from under the client. |
    +| STATUS_ABORTED | 4 | Localization was aborted, likely because of a new request. |
    +| STATUS_FAILED | 5 | Failed to localize for some other reason; see the error_report for details. This is often because the initial guess was incorrect. |
    +| STATUS_FIDUCIAL_TOO_FAR_AWAY | 6 | Failed to localize because the fiducial requested by 'use_fiducial_id' was too far away from the robot. |
    +| STATUS_FIDUCIAL_TOO_OLD | 7 | Failed to localize because the fiducial requested by 'use_fiducial_id' had a detection time that was too far in the past. |
    +| STATUS_NO_MATCHING_FIDUCIAL | 8 | Failed to localize because the fiducial requested by 'use_fiducial_id' did not exist in the map at the required location. |
    +| STATUS_FIDUCIAL_POSE_UNCERTAIN | 9 | Failed to localize because the fiducial requested by 'use_fiducial_id' had an unreliable pose estimation, either in the current detection of that fiducial, or in detections that were saved in the map. Note that when using FIDUCIAL_INIT_SPECIFIC, fiducial detections at the target waypoint will be used so long as they are not uncertain -- otherwise, detections at adjacent waypoints may be used. If there exists no uncertain detection of the fiducial near the target waypoint in the map, the service returns this status. |
    +
    +
    +
    +
    +
    +### TravelParams.FeatureQualityTolerance
    +
    +Indicates whether robot will navigate through areas with poor quality features
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| TOLERANCE_UNKNOWN | 0 | Unknown value |
    +| TOLERANCE_DEFAULT | 1 | Navigate through default number of waypoints with poor quality features |
    +| TOLERANCE_IGNORE_POOR_FEATURE_QUALITY | 2 | Navigate through unlimited number of waypoints with poor quality features |
    +
    +
    +
    +
    +
    +### UploadGraphResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 |  |
    +| STATUS_OK | 1 |  |
    +| STATUS_MAP_TOO_LARGE_LICENSE | 3 | Can't upload the graph because it was too large for the license. |
    +| STATUS_INVALID_GRAPH | 4 | The graph is invalid topologically, for example containing missing waypoints referenced by edges. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# graph_nav/graph_nav_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### GraphNavService
    +
    +The GraphNav service service is a place-based localization and locomotion service. The service can
    +be used to get/set the localization, upload and download the current graph nav maps, and send navigation
    +requests to move around the map.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| SetLocalization | [SetLocalizationRequest](#bosdyn.api.graph_nav.SetLocalizationRequest) | [SetLocalizationResponse](#bosdyn.api.graph_nav.SetLocalizationResponse) | Trigger a manual localization. Typically done to provide the initial localization. |
    +| NavigateRoute | [NavigateRouteRequest](#bosdyn.api.graph_nav.NavigateRouteRequest) | [NavigateRouteResponse](#bosdyn.api.graph_nav.NavigateRouteResponse) | Tell GraphNav to navigate/traverse a given route. |
    +| NavigateTo | [NavigateToRequest](#bosdyn.api.graph_nav.NavigateToRequest) | [NavigateToResponse](#bosdyn.api.graph_nav.NavigateToResponse) | Tell GraphNav to navigate to a waypoint along a route it chooses. |
    +| NavigateToAnchor | [NavigateToAnchorRequest](#bosdyn.api.graph_nav.NavigateToAnchorRequest) | [NavigateToAnchorResponse](#bosdyn.api.graph_nav.NavigateToAnchorResponse) | Tell GraphNav to navigate to a goal with respect to the current anchoring. |
    +| NavigationFeedback | [NavigationFeedbackRequest](#bosdyn.api.graph_nav.NavigationFeedbackRequest) | [NavigationFeedbackResponse](#bosdyn.api.graph_nav.NavigationFeedbackResponse) | Get feedback on active navigation command. |
    +| GetLocalizationState | [GetLocalizationStateRequest](#bosdyn.api.graph_nav.GetLocalizationStateRequest) | [GetLocalizationStateResponse](#bosdyn.api.graph_nav.GetLocalizationStateResponse) | Get the localization status and data. |
    +| ClearGraph | [ClearGraphRequest](#bosdyn.api.graph_nav.ClearGraphRequest) | [ClearGraphResponse](#bosdyn.api.graph_nav.ClearGraphResponse) | Clears the local graph structure. Also erases any snapshots currently in RAM. |
    +| DownloadGraph | [DownloadGraphRequest](#bosdyn.api.graph_nav.DownloadGraphRequest) | [DownloadGraphResponse](#bosdyn.api.graph_nav.DownloadGraphResponse) | Download the graph structure. |
    +| UploadGraph | [UploadGraphRequest](#bosdyn.api.graph_nav.UploadGraphRequest) | [UploadGraphResponse](#bosdyn.api.graph_nav.UploadGraphResponse) | Upload the full list of waypoint IDs, graph topology and other small info. |
    +| UploadWaypointSnapshot | [UploadWaypointSnapshotRequest](#bosdyn.api.graph_nav.UploadWaypointSnapshotRequest) stream | [UploadWaypointSnapshotResponse](#bosdyn.api.graph_nav.UploadWaypointSnapshotResponse) | Uploads large waypoint snapshot as a stream for a particular waypoint. |
    +| UploadEdgeSnapshot | [UploadEdgeSnapshotRequest](#bosdyn.api.graph_nav.UploadEdgeSnapshotRequest) stream | [UploadEdgeSnapshotResponse](#bosdyn.api.graph_nav.UploadEdgeSnapshotResponse) | Uploads large edge snapshot as a stream for a particular edge. |
    +| DownloadWaypointSnapshot | [DownloadWaypointSnapshotRequest](#bosdyn.api.graph_nav.DownloadWaypointSnapshotRequest) | [DownloadWaypointSnapshotResponse](#bosdyn.api.graph_nav.DownloadWaypointSnapshotResponse) stream | Download waypoint data from the server. If the snapshot exists in disk cache, it will be loaded. |
    +| DownloadEdgeSnapshot | [DownloadEdgeSnapshotRequest](#bosdyn.api.graph_nav.DownloadEdgeSnapshotRequest) | [DownloadEdgeSnapshotResponse](#bosdyn.api.graph_nav.DownloadEdgeSnapshotResponse) stream | Download edge data from the server. If the snapshot exists in disk cache, it will be loaded. |
    +
    + 
    +
    +
    +
    +
    +
    +# graph_nav/map.proto
    +
    +
    +
    +
    +
    +### Anchor
    +
    +This associates a waypoint with a common reference frame, which is not necessarily metric.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| id | [string](#string) | Identifier of the waypoint. |
    +| seed_tform_waypoint | [bosdyn.api.SE3Pose](#bosdyn.api.SE3Pose) | Pose of the waypoint in the seed frame. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### AnchoredWorldObject
    +
    +This associates a world object with a common reference frame, which is not necessarily metric.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| id | [string](#string) | Identifier of the world object. |
    +| seed_tform_object | [bosdyn.api.SE3Pose](#bosdyn.api.SE3Pose) | Pose of the object in the seed frame. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Anchoring
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| anchors | [Anchor](#bosdyn.api.graph_nav.Anchor) | The waypoint ids for the graph, expressed in a common reference frame, which is not necessarily metric. If there is no anchoring, this is empty. |
    +| objects | [AnchoredWorldObject](#bosdyn.api.graph_nav.AnchoredWorldObject) | World objects, located in the common reference frame. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Edge
    +
    +A base element of the graph nav map. Edges consist of a directed edge from one
    +waypoint to another and a transform that estimates the relationship in 3D space
    +between the two waypoints.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| id | [Edge.Id](#bosdyn.api.graph_nav.Edge.Id) | Identifier of this Edge. Edges are mutable -- the identifier does not have to be updated when other fields change. |
    +| snapshot_id | [string](#string) | Identifier of this edge's Snapshot data. |
    +| from_tform_to | [bosdyn.api.SE3Pose](#bosdyn.api.SE3Pose) | Describes the transform between the "from" waypoint and the "to" waypoint. |
    +| annotations | [Edge.Annotations](#bosdyn.api.graph_nav.Edge.Annotations) | Annotations specific to the current edge. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Edge.Annotations
    +
    +Annotations understood by BostonDynamics systems.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| vel_limit | [bosdyn.api.SE2VelocityLimit](#bosdyn.api.SE2VelocityLimit) | Velocity limits to use while traversing the edge. These are maxima and minima, NOT target speeds. NOTE: as of 2.4 this is deprecated. Please use mobility_params.vel_limit. |
    +| stairs | [Edge.Annotations.StairData](#bosdyn.api.graph_nav.Edge.Annotations.StairData) | Stairs information/parameters specific to the edge. |
    +| direction_constraint | [Edge.Annotations.DirectionConstraint](#bosdyn.api.graph_nav.Edge.Annotations.DirectionConstraint) | Direction constraints for how the robot must move and the directions it can face when traversing the edge. |
    +| require_alignment | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | If true, the robot must be aligned with the edge in yaw before traversing it. |
    +| flat_ground | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | If true, the edge crosses flat ground and the robot shouldn't try to climb over obstacles. |
    +| ground_mu_hint | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Terrain coefficient of friction user hint. This value must be postive and will clamped if necessary on the robot side. Best suggested values lie in the range between 0.4 and 0.8 (which is the robot's default.) WARNING: deprecated as of 2.1. Use mobility_params instead, which includes ground_mu_hint as part of the terrain_params. |
    +| grated_floor | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | If true, the edge crosses over grated metal. This changes some parameters of the robot's perception system to allow it to see grated floors bettter. WARNING: deprecated as of 2.1. Use mobility_params instead, which includes grated_floor as part of the terrain_params. |
    +| override_mobility_params | [google.protobuf.FieldMask](#google.protobuf.FieldMask) | Overrides the following fields of the mobility parameters to whatever is stored in the map. For example, if this FieldMask contains "stair_hint" and "terrain_params.enable_grated_floor", then the map will be annotated with "stair_hint" and "enable_grated_floor" settings. An empty FieldMask means all fields are active annotations. Note that the more conservative of the velocity limit stored in the mobility parameters and the TravelParams of the entire route will be used for this edge (regardless of what override_mobility_params says). |
    +| mobility_params | [bosdyn.api.spot.MobilityParams](#bosdyn.api.spot.MobilityParams) | Contains terrain parameters, swing height, obstacle avoidance parameters, etc. When the robot crosses this edge, it will use the mobility parameters here. |
    +| cost | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Assign edges a cost; used when finding the "shortest" (lowest cost) path. |
    +| edge_source | [Edge.EdgeSource](#bosdyn.api.graph_nav.Edge.EdgeSource) | How this edge was made. |
    +| disable_alternate_route_finding | [bool](#bool) | If true, disables alternate-route-finding for this edge. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Edge.Annotations.StairData
    +
    +Defines any parameters of the stairs
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| state | [AnnotationState](#bosdyn.api.graph_nav.AnnotationState) | Check this before reading other fields. |
    +| straight_staircase | [bosdyn.api.StraightStaircase](#bosdyn.api.StraightStaircase) | Parameters describing a straight staircase. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Edge.Id
    +
    +An edge is uniquely identified by the waypoints it connects.
    +Two waypoints will only ever be connected by a single edge.
    +That edge is traversable in either direction.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| from_waypoint | [string](#string) | Identifier of the "from" waypoint. |
    +| to_waypoint | [string](#string) | Identifier of the "to" waypoint. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### EdgeSnapshot
    +
    +Relevant data collected along the edge.
    +May be used for automatically generating annotations, for example.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| id | [string](#string) | Identifier of this snapshot. Snapshots are immutable -- if any of the other fields change, this ID must also change. |
    +| stances | [EdgeSnapshot.Stance](#bosdyn.api.graph_nav.EdgeSnapshot.Stance) | Sampling of stances as robot traversed this edge. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### EdgeSnapshot.Stance
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Timestamp of the stance. |
    +| foot_states | [bosdyn.api.FootState](#bosdyn.api.FootState) | List of all the foot positions for a single stance. |
    +| ko_tform_body | [bosdyn.api.SE3Pose](#bosdyn.api.SE3Pose) | KO Body position corresponding to this stance. |
    +| vision_tform_body | [bosdyn.api.SE3Pose](#bosdyn.api.SE3Pose) | Vision Body position corresponding to this stance. |
    +| planar_ground | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | Does this stance correspond to a planar ground region. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Graph
    +
    +This is an arbitrary collection of waypoints and edges. The edges and waypoints are not required
    +to be connected. A waypoint may belong to multiple graphs. This message is used to pass around
    +information about a graph's topology, and is used to serialize map topology to and from files.
    +Note that the graph does not contain any of the waypoint/edge data (which is found in snapshots).
    +Snapshots are stored separately.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| waypoints | [Waypoint](#bosdyn.api.graph_nav.Waypoint) | The waypoints for the graph (containing frames, annotations, and sensor data). |
    +| edges | [Edge](#bosdyn.api.graph_nav.Edge) | The edges connecting the graph's waypoints. |
    +| anchoring | [Anchoring](#bosdyn.api.graph_nav.Anchoring) | The anchoring (mapping from waypoints to their pose in a shared reference frame). |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Waypoint
    +
    +A base element of the graph nav map. A waypoint consists of a reference frame, a name,
    +a unique ID, annotations, and sensor data.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| id | [string](#string) | Identifier of the waypoint. Unique across all maps. This identifier does not have to be updated when its fields change. |
    +| snapshot_id | [string](#string) | Identifier of this waypoint's Snapshot data. |
    +| waypoint_tform_ko | [bosdyn.api.SE3Pose](#bosdyn.api.SE3Pose) | Transform from the KO frame (at time of recording) to the waypoint. |
    +| annotations | [Waypoint.Annotations](#bosdyn.api.graph_nav.Waypoint.Annotations) | Annotations specific to the current waypoint. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Waypoint.Annotations
    +
    +Annotations understood by BostonDynamics systems.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| name | [string](#string) | Human-friendly name of the waypoint. For example, "Kitchen Fridge" |
    +| creation_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | The time that the waypoint was created while recording a map. |
    +| icp_variance | [bosdyn.api.SE3Covariance](#bosdyn.api.SE3Covariance) | Estimate of the variance of ICP when performed at this waypoint, collected at record time. |
    +| scan_match_region | [Waypoint.Annotations.LocalizeRegion](#bosdyn.api.graph_nav.Waypoint.Annotations.LocalizeRegion) | Options for how to localize to a waypoint (if at all). |
    +| waypoint_source | [Waypoint.WaypointSource](#bosdyn.api.graph_nav.Waypoint.WaypointSource) | How this waypoint was made. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Waypoint.Annotations.LocalizeRegion
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| state | [AnnotationState](#bosdyn.api.graph_nav.AnnotationState) | Check this before reading other fields. |
    +| default_region | [Waypoint.Annotations.LocalizeRegion.Default](#bosdyn.api.graph_nav.Waypoint.Annotations.LocalizeRegion.Default) | Oneof field that describes the waypoint's location as a default region (no special features/traits). |
    +| empty | [Waypoint.Annotations.LocalizeRegion.Empty](#bosdyn.api.graph_nav.Waypoint.Annotations.LocalizeRegion.Empty) | Oneof field that describes the waypoint's location as a empty/featureless region. |
    +| circle | [Waypoint.Annotations.LocalizeRegion.Circle2D](#bosdyn.api.graph_nav.Waypoint.Annotations.LocalizeRegion.Circle2D) | Oneof field that describes the waypoint's location as a circular region. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Waypoint.Annotations.LocalizeRegion.Circle2D
    +
    +Indicates the number of meters away we can be from this waypoint we can be before scan
    +matching.
    +- If zero, the default value is used.
    +- If less than zero, no scan matching will be performed at this waypoint.
    +- If greater than zero, scan matching will only be performed if the robot is at most this
    +  far away from the waypoint.
    +Distance calculation is done in the 2d plane with respect to the waypoint.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| dist_2d | [double](#double) | meters. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Waypoint.Annotations.LocalizeRegion.Default
    +
    +Use the default region to localize in.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### Waypoint.Annotations.LocalizeRegion.Empty
    +
    +Do not localize to this waypoint.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### WaypointSnapshot
    +
    +Relevant data collected at the waypoint.
    +May be used for localization or automatically generating annotations, for example.
    +Should be indexed by a waypoint's "snapshot_id" field.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| id | [string](#string) | Identifier of this snapshot. Snapshots are immutable -- if any of the other fields change, this ID must also change. |
    +| images | [bosdyn.api.ImageResponse](#bosdyn.api.ImageResponse) | Any images captured at the waypoint. |
    +| point_cloud | [bosdyn.api.PointCloud](#bosdyn.api.PointCloud) | Aggregated point cloud data. |
    +| objects | [bosdyn.api.WorldObject](#bosdyn.api.WorldObject) | Perception objects seen at snapshot time. |
    +| robot_state | [bosdyn.api.RobotState](#bosdyn.api.RobotState) | Full robot state during snapshot. |
    +| robot_local_grids | [bosdyn.api.LocalGrid](#bosdyn.api.LocalGrid) | Robot grid data. |
    +| is_point_cloud_processed | [bool](#bool) | If true, the point cloud of this snapshot has been processed. |
    +| version_id | [string](#string) | If this snapshot is a modified version of the raw snapshot with the given ID (for example, it has been processed), a new unique ID will we assigned to this field. If the field is empty, this is the raw version of the snapshot. |
    +| has_remote_point_cloud_sensor | [bool](#bool) | If true, the point cloud contains data from a remote point cloud service, such as LIDAR. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### AnnotationState
    +
    +Indicator of whether or not the waypoint and edge annotations are complete and filled out.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| ANNOTATION_STATE_UNKNOWN | 0 | No assertions made about this annotation. |
    +| ANNOTATION_STATE_SET | 1 | This annotation and all of its fields have been deliberately set. |
    +| ANNOTATION_STATE_NONE | 2 | This annotation has been deliberately set to "no annotation" -- any subfields are unset. |
    +
    +
    +
    +
    +
    +### Edge.Annotations.DirectionConstraint
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| DIRECTION_CONSTRAINT_UNKNOWN | 0 | We don't know if there are direction constraints. |
    +| DIRECTION_CONSTRAINT_NO_TURN | 1 | The robot must not turn while walking the edge, but can face either waypoint. |
    +| DIRECTION_CONSTRAINT_FORWARD | 2 | Robot should walk the edge face-first. |
    +| DIRECTION_CONSTRAINT_REVERSE | 3 | Robot should walk the edge rear-first. |
    +| DIRECTION_CONSTRAINT_NONE | 4 | No constraints on which way the robot faces. |
    +
    +
    +
    +
    +
    +### Edge.EdgeSource
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| EDGE_SOURCE_UNKNOWN | 0 |  |
    +| EDGE_SOURCE_ODOMETRY | 1 | Edges with transforms from odometry. |
    +| EDGE_SOURCE_SMALL_LOOP_CLOSURE | 2 | Edges with transforms from a short chain of other edges. |
    +| EDGE_SOURCE_FIDUCIAL_LOOP_CLOSURE | 3 | Edges with transforms from multiple fiducial observations. |
    +| EDGE_SOURCE_ALTERNATE_ROUTE_FINDING | 4 | Edges that may help find alternate routes. |
    +| EDGE_SOURCE_USER_REQUEST | 5 | Created via a CreateEdge RPC. |
    +
    +
    +
    +
    +
    +### Waypoint.WaypointSource
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| WAYPOINT_SOURCE_UNKNOWN | 0 |  |
    +| WAYPOINT_SOURCE_ROBOT_PATH | 1 | Waypoints from the robot's location during recording. |
    +| WAYPOINT_SOURCE_USER_REQUEST | 2 | Waypoints with user-requested placement. |
    +| WAYPOINT_SOURCE_ALTERNATE_ROUTE_FINDING | 3 | Waypoints that may help find alternate routes. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# graph_nav/map_processing.proto
    +
    +
    +
    +
    +
    +### AnchorHintUncertainty
    +
    +Controls how certain the user is of an anchor's pose. If left empty, a reasonable default will be chosen.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| se3_covariance | [bosdyn.api.SE3Covariance](#bosdyn.api.SE3Covariance) | A full 6x6 Gaussian covariance matrix representing uncertainty of an anchoring. |
    +| confidence_bounds | [PoseBounds](#bosdyn.api.graph_nav.PoseBounds) | Represents the 95 percent confidence interval on individual axes. This will be converted to a SE3Covariance internally by creating a diagonal matrix whose elements are informed by the confidence bounds. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### AnchoringHint
    +
    +The user may assign a number of world objects and waypoints a guess at where they are in the seed frame.
    +These hints will be respected by the ProcessAnchoringRequest.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| waypoint_anchors | [WaypointAnchorHint](#bosdyn.api.graph_nav.WaypointAnchorHint) | List of waypoints and hints as to where they are in the seed frame. |
    +| world_objects | [WorldObjectAnchorHint](#bosdyn.api.graph_nav.WorldObjectAnchorHint) | List of world objects and hints as to where they are in the seed frame. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PoseBounds
    +
    +Represents an interval in x, y, z and yaw around some center. Some value x
    +will be within the bounds if  center - x_bounds <= x >= center + x_bounds.
    +If the values are left at zero, the bounds are considered to be unconstrained.
    +The center of the bounds is left implicit, and should be whatever this message
    +is packaged with.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| x_bounds | [double](#double) | Bounds on the x position in meters. |
    +| y_bounds | [double](#double) | Bounds on the y position in meters. |
    +| z_bounds | [double](#double) | Bounds on the z position in meters. |
    +| yaw_bounds | [double](#double) | Bounds on the yaw (rotation around z axis) in radians. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ProcessAnchoringRequest
    +
    +Causes the server to optimize an existing anchoring, or generate a new anchoring for the map using the given parameters.
    +In general, if parameters are not provided, reasonable defaults will be used.
    +The new anchoring will be streamed back to the client, or modified on the server if desired.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Standard request header. |
    +| params | [ProcessAnchoringRequest.Params](#bosdyn.api.graph_nav.ProcessAnchoringRequest.Params) |  |
    +| initial_hint | [AnchoringHint](#bosdyn.api.graph_nav.AnchoringHint) | Initial guess at some number of waypoints and world objects and their anchorings. |
    +| modify_anchoring_on_server | [bool](#bool) | If true, the map currently uploaded to the server will have its anchoring modified. Otherwise, the user is expected to re-upload the anchoring. |
    +| stream_intermediate_results | [bool](#bool) | If true, the anchoring will be streamed back to the user after every iteration. This is useful for debug visualization. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ProcessAnchoringRequest.Params
    +
    +Parameters for procesing an anchoring.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| optimizer_params | [ProcessAnchoringRequest.Params.OptimizerParams](#bosdyn.api.graph_nav.ProcessAnchoringRequest.Params.OptimizerParams) |  |
    +| measurement_params | [ProcessAnchoringRequest.Params.MeasurementParams](#bosdyn.api.graph_nav.ProcessAnchoringRequest.Params.MeasurementParams) |  |
    +| weights | [ProcessAnchoringRequest.Params.Weights](#bosdyn.api.graph_nav.ProcessAnchoringRequest.Params.Weights) |  |
    +| optimize_existing_anchoring | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | If true, the anchoring which already exists on the server will be used as the initial guess for the optimizer. Otherwise, a new anchoring will be generated for every waypoint which doesn't have a value passed in through initial_hint. If no hint is provided, and this value is false, every waypoint will be given a starting anchoring based on the oldest waypoint in the map. |
    +| gravity_ewrt_seed | [bosdyn.api.Vec3](#bosdyn.api.Vec3) | The optimizer will try to keep the orientation of waypoints consistent with gravity. If provided, this is the gravity direction expressed with respect to the seed. This will be interpreted as a unit vector. If not filled out, a default of (0, 0, -1) will be used. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ProcessAnchoringRequest.Params.MeasurementParams
    +
    +Parameters which affect the measurements the optimzier uses to process the anchoring.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| use_kinematic_odometry | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | If true, waypoints which share the same kinematic odometry frame will be constrained to one another using it. |
    +| use_visual_odometry | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | If true, waypoints which share the same visual odometry frame will be constrained to one another using it. |
    +| use_gyroscope_measurements | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | If true, waypoints will be constrained so that the apparent pose of the robot w.r.t the waypoint at the time of recording is consistent with gravity. |
    +| use_loop_closures | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | If true, edges which were created by topology processing via loop closures will be used as constraints. |
    +| use_world_objects | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | If true, world object measurements will be used to constrain waypoints to one another when those waypoints co-observe the same world object. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ProcessAnchoringRequest.Params.OptimizerParams
    +
    +Parameters affecting the underlying optimizer.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| max_iters | [google.protobuf.Int32Value](#google.protobuf.Int32Value) | Maximum iterations of the optimizer to run. |
    +| max_time_seconds | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Maximum time the optimizer is allowed to run before giving up. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ProcessAnchoringRequest.Params.Weights
    +
    +Relative weights to use for each of the optimizer's terms. These can be any positive value.
    +If set to zero, a reasonable default will be used. In general, the higher the weight, the more
    +the optimizer will care about that particular measurement.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| kinematic_odometry_weight | [double](#double) |  |
    +| visual_odometry_weight | [double](#double) |  |
    +| world_object_weight | [double](#double) |  |
    +| hint_weight | [double](#double) |  |
    +| gyroscope_weight | [double](#double) |  |
    +| loop_closure_weight | [double](#double) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ProcessAnchoringResponse
    +
    +Streamed response from the ProcessAnchoringRequest. These will be streamed until optimization is complete.
    +New anchorings will be streamed as they become available.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) |  |
    +| status | [ProcessAnchoringResponse.Status](#bosdyn.api.graph_nav.ProcessAnchoringResponse.Status) |  |
    +| waypoint_results | [Anchor](#bosdyn.api.graph_nav.Anchor) | Contains new anchorings for waypoint(s) processed by the server. These will be streamed back to the user as they become available. |
    +| world_object_results | [AnchoredWorldObject](#bosdyn.api.graph_nav.AnchoredWorldObject) | Contains new anchorings for object(s) (e.g april tags) processed by the server. These will be streamed back to the user as they become available |
    +| anchoring_on_server_was_modified | [bool](#bool) | If modify_anchoring_on_server was set to true in the request, then the anchoring currently on the server was modified using map processing. If this is set to false, then either an error occurred during processing, or modify_anchoring_on_server was set to false in the request. When anchoring_on_server_was_modified is set to false, the client is expected to upload the results back to the server to commit the changes. |
    +| iteration | [int32](#int32) | The current optimizer iteration that produced these data. |
    +| cost | [double](#double) | The current nonlinear optimization cost. |
    +| final_iteration | [bool](#bool) | If true, this is the result of the final iteration of optimization. This will always be true when stream_intermediate_results in the request is false. |
    +| violated_waypoint_constraints | [WaypointAnchorHint](#bosdyn.api.graph_nav.WaypointAnchorHint) | On failure due to constraint violation, these hints were violated by the optimization. Try increasing the pose bounds on the constraints of these hints. |
    +| violated_object_constraints | [WorldObjectAnchorHint](#bosdyn.api.graph_nav.WorldObjectAnchorHint) | On failure due to constraint violation, these hints were violated by the optimization. Try increasing the pose bounds on the constraints of these hints. |
    +| missing_snapshot_ids | [string](#string) | When there are missing waypoint snapshots, these are the IDs of the missing snapshots. Upload them to continue. |
    +| missing_waypoint_ids | [string](#string) | When there are missing waypoints, these are the IDs of the missing waypoints. Upload them to continue. |
    +| invalid_hints | [string](#string) | Unorganized list of waypoints and object IDs which were invalid (missing from the map). |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ProcessTopologyRequest
    +
    +Processes a GraphNav map by creating additional edges. After processing,
    +a new subgraph is created containing additional edges to add to the map.
    +Edges are created between waypoints that are near each other. These waypoint pairs
    +are called "loop closures", and are found by different means.
    +In general, if parameters are not provided, reasonable defaults will be used.
    +Note that this can be used to merge disconnected subgraphs from multiple recording
    +sessions so long as they share fiducial observations.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Standard message header. |
    +| params | [ProcessTopologyRequest.Params](#bosdyn.api.graph_nav.ProcessTopologyRequest.Params) | Parameters. If not filled out, reasonable defaults will be used. |
    +| modify_map_on_server | [bool](#bool) | If true, any processing should directly modify the map on the server. Otherwise, the client is expected to upload the processing results (newly created edges) back to the server. The processing service shares memory with a map container service (e.g the GraphNav service). |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ProcessTopologyRequest.CollisionCheckingParams
    +
    +Parameters for how to check for collisions when creating loop closures. The system
    +will avoid creating edges in the map that the robot cannot actually traverse due to
    +the presence of nearby obstacles.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| check_edges_for_collision | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | By default, this is true. |
    +| collision_check_robot_radius | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Assume that the robot is a sphere with this radius. Only accept a loop closure if this spherical robot can travel in a straight line from one waypoint to the other without hitting obstacles. |
    +| collision_check_height_variation | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Consider significant height variations along the edge (like stairs or ramps) to be obstacles. The edge will not be created if there is a height change along it of more than this value according to the nearby sensor data. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ProcessTopologyRequest.FiducialLoopClosureParams
    +
    +Parameters for how to close a loop using fiducials (AprilTags). This infers
    +which waypoints should be connected to one another based on shared observations
    +of AprilTags.
    +Note that multiple disconnected subgraphs (for example from multiple recording sessions)
    +can be merged this way.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| min_loop_closure_path_length | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The minimum distance between waypoints found by walking a path from one waypoint to the other using only the existing edges in the map. Set this higher to avoid creating small shortcuts along the existing path. Note that this is a 2d path length. |
    +| max_loop_closure_edge_length | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Once a loop closure candidate is found, the system creates an edge between the candidate waypoints. Only create the edge if it is shorter than this value. Note that this is a 3d edge length. |
    +| max_fiducial_distance | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Maximum distance to accept between a waypoint and a fiducial detection to use that fiducial detection for generating loop closure candidates. |
    +| max_loop_closure_height_change | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The maximum apparent height change of the created edge that we are willing to accept between waypoints. This avoids closing loops up ramps, stairs, etc. or closing loops where there is significant odometry drift. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ProcessTopologyRequest.ICPParams
    +
    +Parameters for how to refine loop closure edges using iterative
    +closest point matching.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| icp_iters | [google.protobuf.Int32Value](#google.protobuf.Int32Value) | The maximum number of iterations to run. Set to zero to skip ICP processing. |
    +| max_point_match_distance | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The maximum distance between points in the point cloud we are willing to accept for matches. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ProcessTopologyRequest.OdometryLoopClosureParams
    +
    +Parameters for how to close loops using odometry. This infers which waypoints
    +should be connected to one another based on the odometry measurements in the map.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| max_loop_closure_path_length | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The maximum distance between waypoints found by walking a path from one waypoint to the other using only the existing edges in the map. Beyond this distance, we are unwilling to trust odometry. |
    +| min_loop_closure_path_length | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The minimum distance between waypoints found by walking a path from one waypoint to the other using only the existing edges in the map. Set this higher to avoid creating small shortcuts along the existing path. Note that this is a 2d path length. |
    +| max_loop_closure_height_change | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The maximum apparent height change of the created edge that we are willing to accept between waypoints. This avoids closing loops up ramps, stairs, etc. or closing loops where there is significant odometry drift. |
    +| max_loop_closure_edge_length | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Once a loop closure candidate is found, the system creates an edge between the candidate waypoints. Only create the edge if it is shorter than this value. Note that this is a 3d edge length. |
    +| num_extra_loop_closure_iterations | [google.protobuf.Int32Value](#google.protobuf.Int32Value) | Use prior loop closures to infer new odometry based loop closures. This is useful when other sources of loop closures (like fiducials) are being used. The existence of those loop closures allows the system to infer other nearby loop closures using odometry. Alternatively, the user may call the ProcessTopology RPC multiple times to achieve the same effect. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ProcessTopologyRequest.Params
    +
    +Parameters which control topology processing. In general, anything which isn't filled out
    +will be replaced by reasonable defaults.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| do_odometry_loop_closure | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | True by default -- generate loop closure candidates using odometry. |
    +| odometry_loop_closure_params | [ProcessTopologyRequest.OdometryLoopClosureParams](#bosdyn.api.graph_nav.ProcessTopologyRequest.OdometryLoopClosureParams) | Parameters for generating loop closure candidates using odometry. |
    +| icp_params | [ProcessTopologyRequest.ICPParams](#bosdyn.api.graph_nav.ProcessTopologyRequest.ICPParams) | Parameters for refining loop closure candidates using iterative closest point cloud matching. |
    +| do_fiducial_loop_closure | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | True by default -- generate loop closure candidates using fiducials. |
    +| fiducial_loop_closure_params | [ProcessTopologyRequest.FiducialLoopClosureParams](#bosdyn.api.graph_nav.ProcessTopologyRequest.FiducialLoopClosureParams) | Parameters for generating loop closure candidates using fiducials. |
    +| collision_check_params | [ProcessTopologyRequest.CollisionCheckingParams](#bosdyn.api.graph_nav.ProcessTopologyRequest.CollisionCheckingParams) | Parameters which control rejecting loop closure candidates which collide with obstacles. |
    +| timeout_seconds | [double](#double) | Causes the processing to time out after this many seconds. If not set, a default of 45 seconds will be used. If this timeout occurs before the overall RPC timeout, a partial result will be returned with ProcessTopologyResponse.timed_out set to true. Processing can be continued by calling ProcessTopology again. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ProcessTopologyResponse
    +
    +Result of the topology processing RPC. If successful, contains a subgraph of new
    +waypoints or edges created by this process.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Standard message header. |
    +| status | [ProcessTopologyResponse.Status](#bosdyn.api.graph_nav.ProcessTopologyResponse.Status) | Result of the processing. |
    +| new_subgraph | [Graph](#bosdyn.api.graph_nav.Graph) | This graph contains the new edge(s) created by map processing. Note that these edges will be annotated with their creation method. Note that several subgraphs may be returned via streaming as the map is processed. |
    +| map_on_server_was_modified | [bool](#bool) | If modify_map_on_server was set to true in the request, then the map currently on the server was modified using map processing. If this is set to false, then either an error occurred during processing, or modify_map_on_server was set to false in the request. When map_on_server_was_modified is set to false, the client is expected to upload the results back to the server to commit the changes. |
    +| missing_snapshot_ids | [string](#string) | When there are missing waypoint snapshots, these are the IDs of the missing snapshots. Upload them to continue. |
    +| missing_waypoint_ids | [string](#string) | When there are missing waypoints, these are the IDs of the missing waypoints. Upload them to continue. |
    +| timed_out | [bool](#bool) | If true, the processing timed out. Note that this is not considered an error. Run topology processing again to continue adding edges. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### WaypointAnchorHint
    +
    +Waypoints may be anchored to a particular seed frame. The user may request that a waypoint
    +be anchored in a particular place with some Gaussian uncertainty.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| waypoint_anchor | [Anchor](#bosdyn.api.graph_nav.Anchor) | This is to be interpreted as the mean of a Gaussian distribution, representing the pose of the waypoint in the seed frame. |
    +| seed_tform_waypoint_uncertainty | [AnchorHintUncertainty](#bosdyn.api.graph_nav.AnchorHintUncertainty) | This is the uncertainty of the anchor's pose in the seed frame. If left empty, a reasonable default uncertainty will be generated. |
    +| seed_tform_waypoint_constraint | [PoseBounds](#bosdyn.api.graph_nav.PoseBounds) | Normally, the optimizer will move the anchorings of waypoints based on context, to minimize the overall cost of the optimization problem. By providing a constraint on pose, the user can ensure that the anchors stay within a certain region in the seed frame. Leaving this empty will allow the optimizer to move the anchoring from the hint as far as it likes. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### WorldObjectAnchorHint
    +
    +World objects (such as fiducials) may be anchored to a particular seed frame. The user may request that an object
    +be anchored in a particular place with some Gaussian uncertainty.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| object_anchor | [AnchoredWorldObject](#bosdyn.api.graph_nav.AnchoredWorldObject) | This is to be interpreted as the mean of a Gaussian distribution, representing the pose of the object in the seed frame. |
    +| seed_tform_object_uncertainty | [AnchorHintUncertainty](#bosdyn.api.graph_nav.AnchorHintUncertainty) | This is the uncertainty of the anchor's pose in the seed frame. If left empty, a reasonable default uncertainty will be generated. |
    +| seed_tform_object_constraint | [PoseBounds](#bosdyn.api.graph_nav.PoseBounds) | Normally, the optimizer will move the anchorings of object based on context, to minimize the overall cost of the optimization problem. By providing a constraint on pose, the user can ensure that the anchors stay within a certain region in the seed frame. Leaving this empty will allow the optimizer to move the anchoring from the hint as far as it likes. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### ProcessAnchoringResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Programming error. |
    +| STATUS_OK | 1 | Success. |
    +| STATUS_MISSING_WAYPOINT_SNAPSHOTS | 2 | Not all of the waypoint snapshots exist on the server. Upload them to continue. |
    +| STATUS_INVALID_GRAPH | 3 | The graph is invalid topologically, for example containing missing waypoints referenced by edges. |
    +| STATUS_OPTIMIZATION_FAILURE | 4 | The optimization failed due to local minima or an ill-conditioned problem definition. |
    +| STATUS_INVALID_PARAMS | 5 | The parameters passed to the optimizer do not make sense (e.g negative weights). |
    +| STATUS_CONSTRAINT_VIOLATION | 6 | One or more anchors were moved outside of the desired constraints. |
    +| STATUS_MAX_ITERATIONS | 7 | The optimizer reached the maximum number of iterations before converging. |
    +| STATUS_MAX_TIME | 8 | The optimizer timed out before converging. |
    +| STATUS_INVALID_HINTS | 9 | One or more of the hints passed in to the optimizer are invalid (do not correspond to real waypoints or objects). |
    +| STATUS_MAP_MODIFIED_DURING_PROCESSING | 10 | Tried to write the anchoring after processing, but another client may have modified the map. Try again. |
    +
    +
    +
    +
    +
    +### ProcessTopologyResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Programming error. |
    +| STATUS_OK | 1 | Success. |
    +| STATUS_MISSING_WAYPOINT_SNAPSHOTS | 2 | Not all of the waypoint snapshots exist on the server. Upload them to continue. |
    +| STATUS_INVALID_GRAPH | 3 | The graph is invalid topologically, for example containing missing waypoints referenced by edges. |
    +| STATUS_MAP_MODIFIED_DURING_PROCESSING | 4 | Tried to write the anchoring after processing, but another client may have modified the map. Try again |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# graph_nav/map_processing_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### MapProcessingService
    +
    +Defines services for processing an existing GraphNav map.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| ProcessTopology | [ProcessTopologyRequest](#bosdyn.api.graph_nav.ProcessTopologyRequest) | [ProcessTopologyResponse](#bosdyn.api.graph_nav.ProcessTopologyResponse) stream | Processes a GraphNav map by creating additional edges or waypoints. After processing, a new subgraph is created containing additional waypoints or edges to add to the map. |
    +| ProcessAnchoring | [ProcessAnchoringRequest](#bosdyn.api.graph_nav.ProcessAnchoringRequest) | [ProcessAnchoringResponse](#bosdyn.api.graph_nav.ProcessAnchoringResponse) stream | Processes a GraphNav map by modifying the anchoring of waypoints and world objects in the map with respect to a seed frame. After processing, a new anchoring is streamed back. |
    +
    + 
    +
    +
    +
    +
    +
    +# graph_nav/nav.proto
    +
    +
    +
    +
    +
    +### Localization
    +
    +The localization state of the robot. This reports the pose of the robot relative
    +to a particular waypoint on the graph nav map.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| waypoint_id | [string](#string) | Waypoint this localization is relative to. |
    +| waypoint_tform_body | [bosdyn.api.SE3Pose](#bosdyn.api.SE3Pose) | Pose of body in waypoint frame. |
    +| seed_tform_body | [bosdyn.api.SE3Pose](#bosdyn.api.SE3Pose) | Pose of body in a common reference frame. The common reference frame defaults to the starting fiducial frame, but can be changed. See Anchoring for more info. |
    +| timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Time (in robot time basis) that this localization was valid. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Route
    +
    +Route that the robot should follow or is currently following.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| waypoint_id | [string](#string) | Ordered list of waypoints to traverse, starting from index 0. |
    +| edge_id | [Edge.Id](#bosdyn.api.graph_nav.Edge.Id) | Ordered list of edges to traverse between those waypoints. |
    +
    +
    +
    +
    +
    + 
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# graph_nav/recording.proto
    +
    +
    +
    +
    +
    +### CreateEdgeRequest
    +
    +The CreateEdge request message specifies an edge to create between two existing waypoints.
    +The edge must not already exist in the map. This can be used to close a loop or to add any
    +additional edges.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| edge | [Edge](#bosdyn.api.graph_nav.Edge) | Create an edge between two existing waypoints in the map with the given parameters. |
    +| lease | [bosdyn.api.Lease](#bosdyn.api.Lease) | The recording service is protected by a lease. The client must have a lease to the recording service to modify its internal state. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### CreateEdgeResponse
    +
    +The CreateEdge response message contains the status of this request and any useful error
    +information if the request fails.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [CreateEdgeResponse.Status](#bosdyn.api.graph_nav.CreateEdgeResponse.Status) | Return status for the request. |
    +| error_existing_edge | [Edge](#bosdyn.api.graph_nav.Edge) | If set, the existing edge that caused the STATUS_EXISTS error. |
    +| lease_use_result | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | The results/status of the lease provided. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### CreateWaypointRequest
    +
    +The CreateWaypoint request message specifies a name and environment the robot should
    +use to generate a waypoint in the graph at it's current location.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| waypoint_name | [string](#string) | Name of the waypoint to create. Overrides any naming prefix. |
    +| recording_environment | [RecordingEnvironment](#bosdyn.api.graph_nav.RecordingEnvironment) | This will be merged into a copy of the existing persistent recording environment and used as the environment for the created waypoint and the edge from the previous waypoint to the new one. It will not affect the persistent environment. |
    +| lease | [bosdyn.api.Lease](#bosdyn.api.Lease) | The recording service is protected by a lease. The client must have a lease to the recording service to modify its internal state. |
    +| require_fiducials | [int32](#int32) | If filled out, asks that the record service verify that the given fiducial IDs are presently visible before creating a waypoint. This is useful for verifying that the robot is where the user thinks it is in an area with known fiducials. |
    +| world_objects | [bosdyn.api.WorldObject](#bosdyn.api.WorldObject) | Additional world objects to insert into this waypoint. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### CreateWaypointResponse
    +
    +The CreateWaypoint response message contains the complete waypoint, and the associated
    +edge connecting this waypoint to the graph when the request succeeds.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| created_waypoint | [Waypoint](#bosdyn.api.graph_nav.Waypoint) | The waypoint that was just created. |
    +| created_edge | [Edge](#bosdyn.api.graph_nav.Edge) | The edge connecting the waypoint just created with the last created waypoint in the map. |
    +| status | [CreateWaypointResponse.Status](#bosdyn.api.graph_nav.CreateWaypointResponse.Status) | Return status for the request. |
    +| lease_use_result | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | The results/status of the lease provided. |
    +| missing_fiducials | [int32](#int32) | If the status is STATUS_MISSING_FIDUCIALS, the following fiducials were not visible to the robot when trying to create the waypoint. |
    +| bad_pose_fiducials | [int32](#int32) | If the status is STATUS_FIDUCIAL_POSE_NOT_OK, these are the fiducials that could not be localized confidently. |
    +| license_status | [bosdyn.api.LicenseInfo.Status](#bosdyn.api.LicenseInfo.Status) | Large graphs can only be uploaded if the license permits them. Recording will stop automatically when the graph gets too large. If CreateWaypointResponse is requested after the graph gets too large, it will fail, and license status will be filled out. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetRecordStatusRequest
    +
    +The GetRecordStatus request message asks for the current state of the recording service.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetRecordStatusResponse
    +
    +The GetRecordStatus response message returns whether the service is currently recording and what the
    +persistent recording environment is at the time the request was recieved.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| is_recording | [bool](#bool) | If true, the record service is actively recording a chain. |
    +| recording_environment | [RecordingEnvironment](#bosdyn.api.graph_nav.RecordingEnvironment) | The current persistent recording environment. |
    +| map_state | [GetRecordStatusResponse.MapState](#bosdyn.api.graph_nav.GetRecordStatusResponse.MapState) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RecordingEnvironment
    +
    +The RecordingEnvironment is a set of annotation information and a name for the
    +current environment that will persist for all edges and waypoints until it is
    +changed or updated
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| name_prefix | [string](#string) | This will be prepended to the start of every waypoint name. |
    +| waypoint_environment | [Waypoint.Annotations](#bosdyn.api.graph_nav.Waypoint.Annotations) | Persistent waypoint annotation that will be merged in to all waypoints in this recording. |
    +| edge_environment | [Edge.Annotations](#bosdyn.api.graph_nav.Edge.Annotations) | Persistent edge annotation that will be merged in to all waypoints in this recording. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetRecordingEnvironmentRequest
    +
    +The SetRecordingEnvironment request message sets a persistent recording environment
    +until changed with another SetRecordingEnvironment rpc.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| environment | [RecordingEnvironment](#bosdyn.api.graph_nav.RecordingEnvironment) | Persistent environment to use while recording. This allows the user to specify annotations and naming prefixes for new waypoints and edges. |
    +| lease | [bosdyn.api.Lease](#bosdyn.api.Lease) | The recording service is protected by a lease. The client must have a lease to the recording service to modify its internal state. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetRecordingEnvironmentResponse
    +
    +The SetRecordingEnvironment response message includes the result and status of the request.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| lease_use_result | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | The results/status of the lease provided. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StartRecordingRequest
    +
    +The StartRecording request tells the recording service to begin creating waypoints with the
    +specified recording_environment.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| lease | [bosdyn.api.Lease](#bosdyn.api.Lease) | The recording service is protected by a lease. The client must have a lease to the recording service to modify its internal state. |
    +| recording_environment | [RecordingEnvironment](#bosdyn.api.graph_nav.RecordingEnvironment) | This will be merged into a copy of the existing persistent recording environment and used as the environment for the created waypoint and the edge from the previous waypoint to the new one. It will not affect the persistent environment. |
    +| require_fiducials | [int32](#int32) | If filled out, asks that the record service verify that the given fiducial IDs are presently visible before starting to record. This is useful for verifying that the robot is where the user thinks it is in an area with known fiducials. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StartRecordingResponse
    +
    +The StartRecording response messge returns the first created waypoint, which is made at the location
    +the robot was standing when the request was made, in addition to any status information.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| created_waypoint | [Waypoint](#bosdyn.api.graph_nav.Waypoint) | The waypoint that was just created. |
    +| lease_use_result | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | The results/status of the lease provided. |
    +| status | [StartRecordingResponse.Status](#bosdyn.api.graph_nav.StartRecordingResponse.Status) | Return status for the request. |
    +| missing_fiducials | [int32](#int32) | If the status is STATUS_MISSING_FIDUCIALS, these are the fiducials that are not currently visible. |
    +| bad_pose_fiducials | [int32](#int32) | If the status is STATUS_FIDUCIAL_POSE_NOT_OK, these are the fiducials that could not be localized confidently. |
    +| license_status | [bosdyn.api.LicenseInfo.Status](#bosdyn.api.LicenseInfo.Status) | Large graphs can only be uploaded if the license permits them. Recording will stop automatically when the graph gets too large. If StartRecording is requested again after the graph gets too large, it will fail, and license status will be filled out. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StopRecordingRequest
    +
    +The StopRecording request message tells the robot to no longer continue adding waypoints and
    +edges to the graph.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| lease | [bosdyn.api.Lease](#bosdyn.api.Lease) | The recording service is protected by a lease. The client must have a lease to the recording service to modify its internal state. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StopRecordingResponse
    +
    +The StopRecording response message contains the status of this request and any useful error
    +information if the request fails.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [StopRecordingResponse.Status](#bosdyn.api.graph_nav.StopRecordingResponse.Status) | The return status for the request. |
    +| error_waypoint_localized_id | [string](#string) | If not localized to end, specifies which waypoint we are localized to. |
    +| lease_use_result | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | The results/status of the lease provided. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### CreateEdgeResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Status is unknown/unset. |
    +| STATUS_OK | 1 | The edge was successfully created. |
    +| STATUS_EXISTS | 2 | Edge already exists with the given ID. |
    +| STATUS_NOT_RECORDING | 3 | Clients can only create edges when recording. |
    +| STATUS_UNKNOWN_WAYPOINT | 4 | One or more of the specified waypoints aren't in the map. |
    +| STATUS_MISSING_TRANSFORM | 5 | Specified edge did not include a transform. |
    +
    +
    +
    +
    +
    +### CreateWaypointResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Status is unknown/unset. |
    +| STATUS_OK | 1 | The waypoint was successfully created. |
    +| STATUS_NOT_RECORDING | 2 | Clients can only create waypoints when recording. |
    +| STATUS_COULD_NOT_CREATE_WAYPOINT | 3 | An internal server error prevented the creation of the waypoint. |
    +| STATUS_MISSING_FIDUCIALS | 4 | Could not see the required fiducials. |
    +| STATUS_MAP_TOO_LARGE_LICENSE | 5 | The map was too big to create a waypoint based on the license. |
    +| STATUS_REMOTE_CLOUD_FAILURE_NOT_IN_DIRECTORY | 6 | A required remote cloud did not exist in the service directory. |
    +| STATUS_REMOTE_CLOUD_FAILURE_NO_DATA | 7 | A required remote cloud did not have data. |
    +| STATUS_FIDUCIAL_POSE_NOT_OK | 8 | All fiducials are visible but their pose could not be determined accurately. |
    +
    +
    +
    +
    +
    +### GetRecordStatusResponse.MapState
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| MAP_STATE_UNKNOWN | 0 |  |
    +| MAP_STATE_OK | 1 | Successfully started recording. |
    +| MAP_STATE_TOO_LARGE_FOR_LICENSE | 2 | Unable to continue recording because a larger map requires an upgraded license. |
    +
    +
    +
    +
    +
    +### StartRecordingResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Status is unknown/unset. |
    +| STATUS_OK | 1 | Recording has been started. |
    +| STATUS_COULD_NOT_CREATE_WAYPOINT | 2 | In this case we tried to start recording, but GraphNav was internally still waiting for some data from the robot. |
    +| STATUS_FOLLOWING_ROUTE | 3 | Can't start recording because the robot is following a route. |
    +| STATUS_NOT_LOCALIZED_TO_EXISTING_MAP | 4 | When recording branches, the robot is not localized to the existing map before starting to record a new branch. |
    +| STATUS_MISSING_FIDUCIALS | 5 | Can't start recording because the robot doesn't see the required fiducials. |
    +| STATUS_MAP_TOO_LARGE_LICENSE | 6 | Can't start recording because the map was too large for the license. |
    +| STATUS_REMOTE_CLOUD_FAILURE_NOT_IN_DIRECTORY | 7 | A required remote cloud did not exist in the service directory. |
    +| STATUS_REMOTE_CLOUD_FAILURE_NO_DATA | 8 | A required remote cloud did not have data. |
    +| STATUS_FIDUCIAL_POSE_NOT_OK | 9 | All fiducials are visible but at least one pose could not be determined accurately. |
    +| STATUS_TOO_FAR_FROM_EXISTING_MAP | 10 | When recording branches, the robot is too far from the existing map when starting to record a new branch. |
    +
    +
    +
    +
    +
    +### StopRecordingResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Status is unknown/unset. |
    +| STATUS_OK | 1 | Recording is stopped. |
    +| STATUS_NOT_LOCALIZED_TO_END | 2 | In this case we tried to stop recording, but had an incorrect localization. graph_nav is expected to be localized to the final waypoint in the chain before we stop recording. |
    +| STATUS_NOT_READY_YET | 3 | The robot is still processing the map it created to where the robot is currently located. You can't stop recording until that processing is finished. You should not move the robot, then try to stop recording again after 1-2 seconds. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# graph_nav/recording_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### GraphNavRecordingService
    +
    +The recording service can be used to record a Graph Nav map (containing waypoints and edges).
    +The recorded map can consist of the following:
    +* Chain: a topological arrangement of waypoints/edges where every waypoint has at least 1
    +but at most 2 edges attached to it.
    +* Branch: separate Chains can be joined together into a Branch at exactly one waypoint.
    +When recording a map using the recording service, a common pattern is:
    +* Call StartRecording to begin recording a chain of waypoints.
    +* Call SetRecordingEnvironment to define persistent annotations for the edges and waypoints.
    +* While recording, call GetRecordStatus to get feedback on the state of the recording service.
    +* While recording, call GetMapStatus to determine what waypoints have been created.
    +* Optionally call CreateWaypoint to create waypoints in specific locations.
    +* Call StopRecording to pause the recording service and create branches.
    +* While recording (or after completing recording), call DownloadWaypoint/Edge Snapshot rpc's
    +from the GraphNavService to download the large sensor data with the map.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| StartRecording | [StartRecordingRequest](#bosdyn.api.graph_nav.StartRecordingRequest) | [StartRecordingResponse](#bosdyn.api.graph_nav.StartRecordingResponse) | Start recording the map from the current localization. Creates a waypoint if you are starting to record. Otherwise, waits until you are sufficiently far away from the previous waypoint. |
    +| StopRecording | [StopRecordingRequest](#bosdyn.api.graph_nav.StopRecordingRequest) | [StopRecordingResponse](#bosdyn.api.graph_nav.StopRecordingResponse) | Stop recording the map from the current localization. |
    +| CreateWaypoint | [CreateWaypointRequest](#bosdyn.api.graph_nav.CreateWaypointRequest) | [CreateWaypointResponse](#bosdyn.api.graph_nav.CreateWaypointResponse) | Create a new waypoint at the current localization. |
    +| SetRecordingEnvironment | [SetRecordingEnvironmentRequest](#bosdyn.api.graph_nav.SetRecordingEnvironmentRequest) | [SetRecordingEnvironmentResponse](#bosdyn.api.graph_nav.SetRecordingEnvironmentResponse) | Set the environmnent and name prefix to use for the recording. |
    +| CreateEdge | [CreateEdgeRequest](#bosdyn.api.graph_nav.CreateEdgeRequest) | [CreateEdgeResponse](#bosdyn.api.graph_nav.CreateEdgeResponse) | Create an arbitrary edge between two waypoints. |
    +| GetRecordStatus | [GetRecordStatusRequest](#bosdyn.api.graph_nav.GetRecordStatusRequest) | [GetRecordStatusResponse](#bosdyn.api.graph_nav.GetRecordStatusResponse) | Tells the client the internal state of the record service, and the structure of the map that has been recorded so far. |
    +
    + 
    +
    +
    +
    +
    +
    +# gripper_command.proto
    +
    +
    +
    +
    +
    +### ClawGripperCommand
    +
    +Command to open and close the gripper.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### ClawGripperCommand.Feedback
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| status | [ClawGripperCommand.Feedback.Status](#bosdyn.api.ClawGripperCommand.Feedback.Status) | Current status of the command. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ClawGripperCommand.Request
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| trajectory | [ScalarTrajectory](#bosdyn.api.ScalarTrajectory) | Scalar trajectory for opening/closing the gripper. If 1 point is specified with no end time, we will execute a minimum time trajectory that observes velocity and acceleration constraints. Otherwise, we will use piecewise cubic interpolation, meaning there will be a cubic polynomial between each trajectory point, with continuous position and velocity at each trajectory point. If the requested trajectory violates the velocity or acceleration constraints below, a trajectory that is as close as possible but still obeys the constraints will be executed instead. position is radians: 0 is fully closed -1.5708 (-90 degrees) is fully open velocity is radians / sec. |
    +| maximum_open_close_velocity | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | If unspecified, a default value of 10 (rad/s) will be used. |
    +| maximum_open_close_acceleration | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | If unspecified, a default value of 40 (rad/s/s) will be used. |
    +| maximum_torque | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Maximum torque applied. If unspecified, a default value of 5.5 (Nm) will be used. |
    +| disable_force_on_contact | [bool](#bool) | By default the gripper transitions to force control when it detects an object closing. Setting this field to true disables the transition to force control on contact detection and always keeps the gripper in position control. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GripperCommand
    +
    +The synchronized command message for commanding the gripper to move.
    +A synchronized commands is one of the possible robot command messages for controlling the robot.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### GripperCommand.Feedback
    +
    +The feedback for the gripper command that will provide information on the progress
    +of the command.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| claw_gripper_feedback | [ClawGripperCommand.Feedback](#bosdyn.api.ClawGripperCommand.Feedback) | Feedback for the claw gripper command. |
    +| status | [RobotCommandFeedbackStatus.Status](#bosdyn.api.RobotCommandFeedbackStatus.Status) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GripperCommand.Request
    +
    +The gripper request must be one of the basic command primitives.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| claw_gripper_command | [ClawGripperCommand.Request](#bosdyn.api.ClawGripperCommand.Request) | Control opening and closing the gripper. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### ClawGripperCommand.Feedback.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | STATUS_UNKNOWN should never be used. If used, an internal error has happened. |
    +| STATUS_IN_PROGRESS | 1 | The gripper is opening or closing. |
    +| STATUS_AT_GOAL | 2 | The gripper is at the final point of the trajectory. |
    +| STATUS_APPLYING_FORCE | 3 | During a close, detected contact and transitioned to force control. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# header.proto
    +
    +
    +
    +
    +
    +### CommonError
    +
    +General error code are returned in the header to facilitate error-handling which is not
    +message-specific.
    +This can be used for generic error handlers, aggregation, and trend analysis.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| code | [CommonError.Code](#bosdyn.api.CommonError.Code) | The different error codes that can be returned on a grpc response message. |
    +| message | [string](#string) | Human-readable error description. Not for programmatic analysis. |
    +| data | [google.protobuf.Any](#google.protobuf.Any) | Extra information that can optionally be provided for generic error handling/analysis. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RequestHeader
    +
    +Standard header attached to all GRPC requests to services.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| request_timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Time that the request was sent, as measured by the client's local system clock. |
    +| client_name | [string](#string) | Name of the client to identify itself. The name will typically include a symbolic string to identify the program, and a unique integer to identify the specific instance of the process running. |
    +| disable_rpc_logging | [bool](#bool) | If Set to true, request that request and response messages for this call are not recorded in the GRPC log. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ResponseHeader
    +
    +Standard header attached to all GRPC responses from services.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| request_header | [RequestHeader](#bosdyn.api.RequestHeader) | Echo-back the RequestHeader for timing information, etc.... |
    +| request_received_timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Time that the request was received. The server clock is the time basis. |
    +| response_timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Time that the response was received. The server clock is the time basis. |
    +| error | [CommonError](#bosdyn.api.CommonError) | Common errors, such as invalid input or internal server problems. If there is a common error, the rest of the response message outside of the ResponseHeader will be invalid. |
    +| request | [google.protobuf.Any](#google.protobuf.Any) | Echoed request message. In some cases it may not be present, or it may be a stripped down representation of the request. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### CommonError.Code
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| CODE_UNSPECIFIED | 0 | Code is not specified. |
    +| CODE_OK | 1 | Not an error. Request was successful. |
    +| CODE_INTERNAL_SERVER_ERROR | 2 | Service experienced an unexpected error state. |
    +| CODE_INVALID_REQUEST | 3 | Ill-formed request. Request arguments were not valid. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# image.proto
    +
    +
    +
    +
    +
    +### CaptureParameters
    +
    +Sensor parameters associated with an image capture.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| exposure_duration | [google.protobuf.Duration](#google.protobuf.Duration) | The duration of exposure in microseconds. |
    +| gain | [double](#double) | Sensor gain in dB. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetImageRequest
    +
    +The GetImage request message which can send multiple different image source requests at once.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| image_requests | [ImageRequest](#bosdyn.api.ImageRequest) | The different image requests for this rpc call. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetImageResponse
    +
    +The GetImage response message which includes image data for all requested sources.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| image_responses | [ImageResponse](#bosdyn.api.ImageResponse) | The ordering of these image responses is defined by the order of the ImageRequests. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Image
    +
    +Rectangular color/greyscale/depth images.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| cols | [int32](#int32) | Number of columns in the image (in pixels). |
    +| rows | [int32](#int32) | Number of rows in the image (in pixels). |
    +| data | [bytes](#bytes) | Raw image data. |
    +| format | [Image.Format](#bosdyn.api.Image.Format) | How the image is encoded. |
    +| pixel_format | [Image.PixelFormat](#bosdyn.api.Image.PixelFormat) | Pixel format of the image; this will be set even when the Format implies the pixel format. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ImageCapture
    +
    +Rectangular color/greyscale images.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| acquisition_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | The time at which the image data was acquired in the robot's time basis. |
    +| transforms_snapshot | [FrameTreeSnapshot](#bosdyn.api.FrameTreeSnapshot) | A tree-based collection of transformations, which will include the transformations to each image's sensor in addition to transformations to the common frames ("vision", "body", "odom"). All transforms within the snapshot are at the acquistion time of the image. |
    +| frame_name_image_sensor | [string](#string) | The frame name for the image's sensor source. This will be included in the transform snapshot. |
    +| image | [Image](#bosdyn.api.Image) | Image data. |
    +| capture_params | [CaptureParameters](#bosdyn.api.CaptureParameters) | Sensor parameters associated with this image capture. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ImageRequest
    +
    +The image request specifying the image source and data format desired.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| image_source_name | [string](#string) | The string name of the image source to get image data from. |
    +| quality_percent | [double](#double) | Image quality: a number from 0 (worst) to 100 (highest). Note that jpeg quality 100 is still lossy. |
    +| image_format | [Image.Format](#bosdyn.api.Image.Format) | Specify the desired image encoding (e.g. JPEG, RAW). If no format is specified (e.g. FORMAT_UNKNOWN), the image service will choose the best format for the data. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ImageResponse
    +
    +The image response for each request, that includes image data and image source information.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| shot | [ImageCapture](#bosdyn.api.ImageCapture) | The image capture contains the image data and information about the state of the camera and robot at the time the image was collected. |
    +| source | [ImageSource](#bosdyn.api.ImageSource) | The source describes general information about the camera source the image data was collected from. |
    +| status | [ImageResponse.Status](#bosdyn.api.ImageResponse.Status) | Return status of the request. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ImageSource
    +
    +Proto for a description of an image source on the robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| name | [string](#string) | The name of this image source used to get images. |
    +| cols | [int32](#int32) | Number of columns in the image (in pixels). |
    +| rows | [int32](#int32) | Number of rows in the image (in pixels). |
    +| depth_scale | [double](#double) | The depth scale for the image data. Typically 1000, which converts it from mm to m. |
    +| pinhole | [ImageSource.PinholeModel](#bosdyn.api.ImageSource.PinholeModel) | Rectilinear camera model. |
    +| image_type | [ImageSource.ImageType](#bosdyn.api.ImageSource.ImageType) | The kind of images returned by this image source. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ImageSource.PinholeModel
    +
    +The camera can be modeled as a pinhole camera described with a matrix.
    +Camera Matrix can be constructed by the camera intrinsics:
    +[[focal_length.x,         skew.x, principal_point.x],
    +[[        skew.y, focal_length.y, principal_point.y],
    +[[             0,              0,                 1]]
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| intrinsics | [ImageSource.PinholeModel.CameraIntrinsics](#bosdyn.api.ImageSource.PinholeModel.CameraIntrinsics) | The camera intrinsics are necessary for descrbing the pinhole camera matrix. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ImageSource.PinholeModel.CameraIntrinsics
    +
    +Intrinsic parameters are in pixel space.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| focal_length | [Vec2](#bosdyn.api.Vec2) | The focal length of the camera. |
    +| principal_point | [Vec2](#bosdyn.api.Vec2) | The optical center in sensor coordinates. |
    +| skew | [Vec2](#bosdyn.api.Vec2) | The skew for the intrinsic matrix. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListImageSourcesRequest
    +
    +The ListImageSources request message for the robot image service.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListImageSourcesResponse
    +
    +The ListImageSources response message which contains all known image sources for the robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response Header. |
    +| image_sources | [ImageSource](#bosdyn.api.ImageSource) | The set of ImageSources available from this service. May be empty if the service serves no cameras (e.g., if no cameras were found on startup). |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### Image.Format
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| FORMAT_UNKNOWN | 0 | Unknown image format. |
    +| FORMAT_JPEG | 1 | Color/greyscale formats. JPEG format. |
    +| FORMAT_RAW | 2 | Uncompressed. Requires pixel_format. |
    +| FORMAT_RLE | 3 | 1 byte run-length before each pixel value. |
    +
    +
    +
    +
    +
    +### Image.PixelFormat
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| PIXEL_FORMAT_UNKNOWN | 0 | Unspecified value -- should not be used. |
    +| PIXEL_FORMAT_GREYSCALE_U8 | 1 | One byte per pixel. |
    +| PIXEL_FORMAT_RGB_U8 | 3 | Three bytes per pixel. |
    +| PIXEL_FORMAT_RGBA_U8 | 4 | Four bytes per pixel. |
    +| PIXEL_FORMAT_DEPTH_U16 | 5 | Little-endian uint16 z-distance from camera (mm). |
    +| PIXEL_FORMAT_GREYSCALE_U16 | 6 | Two bytes per pixel. |
    +
    +
    +
    +
    +
    +### ImageResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | UNKNOWN should never be used. An internal ImageService issue has happened if UNKNOWN is set. None of the other fields are filled out. |
    +| STATUS_OK | 1 | Call succeeded at filling out all the fields. |
    +| STATUS_UNKNOWN_CAMERA | 2 | Image source name in request is unknown. Other fields are not filled out. |
    +| STATUS_SOURCE_DATA_ERROR | 3 | Failed to fill out ImageSource. All the other fields are not filled out. |
    +| STATUS_IMAGE_DATA_ERROR | 4 | There was a problem with the image data. Only the ImageSource is filled out. |
    +| STATUS_UNSUPPORTED_IMAGE_FORMAT_REQUESTED | 5 | The requested image format is unsupported for the image-source named. The image data will not be filled out. Note, if an image request has "FORMAT_UNKNOWN", the service should choose the best format to provide the data in. |
    +
    +
    +
    +
    +
    +### ImageSource.ImageType
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| IMAGE_TYPE_UNKNOWN | 0 | Unspecified image type. |
    +| IMAGE_TYPE_VISUAL | 1 | Color or greyscale intensity image. |
    +| IMAGE_TYPE_DEPTH | 2 | Pixel values represent distances to objects/surfaces. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# image_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### ImageService
    +
    +An Image service provides access to one or more images, for example from cameras. It
    +supports querying for the list of available images provided by the service and then
    +supports requesting a latest given image by source name.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| ListImageSources | [ListImageSourcesRequest](#bosdyn.api.ListImageSourcesRequest) | [ListImageSourcesResponse](#bosdyn.api.ListImageSourcesResponse) | Obtain the list of ImageSources for this given service. Note that there may be multiple ImageServices running, each with their own set of sources The name field keys access to individual images when calling GetImage. |
    +| GetImage | [GetImageRequest](#bosdyn.api.GetImageRequest) | [GetImageResponse](#bosdyn.api.GetImageResponse) | Request an image by name, with optional parameters for requesting image quality level. |
    +
    + 
    +
    +
    +
    +
    +
    +# ir_enable_disable.proto
    +
    +
    +
    +
    +
    +### IREnableDisableRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | < Common request header. |
    +| request | [IREnableDisableRequest.Request](#bosdyn.api.IREnableDisableRequest.Request) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### IREnableDisableResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | < Common response header. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### IREnableDisableRequest.Request
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| REQUEST_UNKNOWN | 0 | Unspecified value -- should not be used. |
    +| REQUEST_OFF | 1 | Disable emissions. |
    +| REQUEST_ON | 2 | Enable emissions. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# ir_enable_disable_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### IREnableDisableService
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| IREnableDisable | [IREnableDisableRequest](#bosdyn.api.IREnableDisableRequest) | [IREnableDisableResponse](#bosdyn.api.IREnableDisableResponse) |  |
    +
    + 
    +
    +
    +
    +
    +
    +# lease.proto
    +
    +
    +
    +
    +
    +### AcquireLeaseRequest
    +
    +The AcquireLease request message which sends which resource the lease should be for.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| resource | [string](#string) | The resource to obtain a Lease for. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### AcquireLeaseResponse
    +
    +The AcquireLease response returns the lease for the desired resource if it could be obtained.
    +If a client is returned a new lease, the client should initiate a
    +RetainLease bidirectional streaming request immediately after completion
    +of AcquireLease.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response Header. |
    +| status | [AcquireLeaseResponse.Status](#bosdyn.api.AcquireLeaseResponse.Status) | Return status for the request. |
    +| lease | [Lease](#bosdyn.api.Lease) | The lease for the resource. Only set if status field == STATUS_OK. |
    +| lease_owner | [LeaseOwner](#bosdyn.api.LeaseOwner) | The owner for the lease. Set if status field == OK or status field == RESOURCE_ALREADY_CLAIMED. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Lease
    +
    +Leases are used to verify that a client has exclusive access to a shared
    +resources. Examples of shared resources are the motors for a robot, or
    +indicator lights on a robot.
    +Leases are initially obtained by clients from the LeaseService. Clients
    +then attach Leases to Commands which require them. Clients may also
    +generate sub-Leases to delegate out control of the resource to other
    +services.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| resource | [string](#string) | The resource that the Lease is for. |
    +| epoch | [string](#string) | The epoch for the Lease. The sequences field are scoped to a particular epoch. One example of where this can be used is to generate a random epoch at LeaseService startup. |
    +| sequence | [uint32](#uint32) | Logical vector clock indicating when the Lease was generated. |
    +| client_names | [string](#string) | The set of different clients which have sent/receieved the lease. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### LeaseOwner
    +
    +Details about who currently owns the Lease for a resource.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| client_name | [string](#string) | The name of the client application. |
    +| user_name | [string](#string) | The name of the user. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### LeaseResource
    +
    +Describes all information about a sepcific lease: including the resource it covers, the
    +active lease, and which application is the owner of a lease.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| resource | [string](#string) | The resource name. |
    +| lease | [Lease](#bosdyn.api.Lease) | The active lease, if any. |
    +| lease_owner | [LeaseOwner](#bosdyn.api.LeaseOwner) | The Lease Owner, if there is a Lease. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### LeaseUseResult
    +
    +Result for when a Lease is used - for example, in a LeaseRetainer, or
    +associated with a command.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| status | [LeaseUseResult.Status](#bosdyn.api.LeaseUseResult.Status) |  |
    +| owner | [LeaseOwner](#bosdyn.api.LeaseOwner) | The current lease owner. |
    +| attempted_lease | [Lease](#bosdyn.api.Lease) | The lease which was attempted for use. |
    +| previous_lease | [Lease](#bosdyn.api.Lease) | The previous lease, if any, which was used. |
    +| latest_known_lease | [Lease](#bosdyn.api.Lease) | The "latest"/"most recent" lease known to the system. |
    +| latest_resources | [Lease](#bosdyn.api.Lease) | Represents the latest "leaf" resources of the hierarchy. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListLeasesRequest
    +
    +The ListLease request message asks for information about any known lease resources.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| include_full_lease_info | [bool](#bool) | Include the full data of leases in use, if available. Defaults to false to receive basic information. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListLeasesResponse
    +
    +The ListLease response message returns all known lease resources from the LeaseService.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| resources | [LeaseResource](#bosdyn.api.LeaseResource) | The resources managed by the LeaseService. |
    +| resource_tree | [ResourceTree](#bosdyn.api.ResourceTree) | Provide the hierarchical lease structure. A resource can encapsulate multiple sub-resources. For example, the "body" lease may include control of the legs, arm, and gripper. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ResourceTree
    +
    +Lease resources can be divided into a hierarchy of sub-resources that can
    +be commanded together. This message describes the hierarchy of a resource.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| resource | [string](#string) | The name of this resource. |
    +| sub_resources | [ResourceTree](#bosdyn.api.ResourceTree) | Sub-resources that make up this resource. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RetainLeaseRequest
    +
    +The RetainLease request will inform the LeaseService that the application contains to hold
    +ownership of this lease. Lease holders are expected to be reachable and alive. If enough time
    +has passed since the last RetainLeaseRequest, the LeaseService will revoke the lease.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| lease | [Lease](#bosdyn.api.Lease) | The Lease to retain ownership over. May also be a "super" lease of the lease to retain ownership over. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RetainLeaseResponse
    +
    +The RetainLease response message sends the result of the attempted RetainLease request, which
    +contains whether or not the lease is still owned by the application sending the request.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| lease_use_result | [LeaseUseResult](#bosdyn.api.LeaseUseResult) | Result of using the lease. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ReturnLeaseRequest
    +
    +The ReturnLease request message will be sent to the LeaseService. If the lease
    +is currently active for the resource, the LeaseService will invalidate the lease.
    +Future calls to AcquireLease by any client will now succeed.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| lease | [Lease](#bosdyn.api.Lease) | The Lease to return back to the LeaseService. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ReturnLeaseResponse
    +
    +The ReturnLease response message
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [ReturnLeaseResponse.Status](#bosdyn.api.ReturnLeaseResponse.Status) | Return status for the request. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### TakeLeaseRequest
    +
    +The TakeLease request message which sends which resource the lease should be for.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| resource | [string](#string) | The resource to obtain a Lease for. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### TakeLeaseResponse
    +
    +The TakeLease response returns the lease for the desired resource if it could be obtained.
    +In most cases if the resource is managed by the LeaseService, TakeLease
    +will succeed. However, in the future policies may be introduced which will prevent
    +TakeLease from succeeding and clients should be prepared to handle that
    +case.
    +If a client obtains a new lease, the client should initiate a
    +RetainLease bidirectional streaming request immediately after completion
    +of TakeLease.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [TakeLeaseResponse.Status](#bosdyn.api.TakeLeaseResponse.Status) | Return status for the request. |
    +| lease | [Lease](#bosdyn.api.Lease) | The lease for the resource. Only set if status field == STATUS_OK. |
    +| lease_owner | [LeaseOwner](#bosdyn.api.LeaseOwner) | The owner for the lease. Set if status field == STATUS_OK. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### AcquireLeaseResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | UNKNOWN should never be used. An internal LeaseService issue has happened if UNKNOWN is set. |
    +| STATUS_OK | 1 | AcquireLease was successful.The lease field will be populated with the new lease for the resource. The client is expected to call the RetainLease method immediately after. |
    +| STATUS_RESOURCE_ALREADY_CLAIMED | 2 | AcquireLease failed since the resource has already been claimed. The TakeLease method may be used to forcefully grab the lease. |
    +| STATUS_INVALID_RESOURCE | 3 | AcquireLease failed since the resource is not known to LeaseService. The ListLeaseResources method may be used to list all known resources. |
    +| STATUS_NOT_AUTHORITATIVE_SERVICE | 4 | The LeaseService is not authoritative - so Acquire should not work. |
    +
    +
    +
    +
    +
    +### LeaseUseResult.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | An internal issue occurred. |
    +| STATUS_OK | 1 | The Lease was accepted. |
    +| STATUS_INVALID_LEASE | 2 | The Lease is invalid. |
    +| STATUS_OLDER | 3 | The Lease is older than the current lease, and rejected. |
    +| STATUS_REVOKED | 4 | The Lease holder did not check in regularly enough, and the Lease is stale. |
    +| STATUS_UNMANAGED | 5 | The Lease was for an unmanaged resource. |
    +| STATUS_WRONG_EPOCH | 6 | The Lease was for the wrong epoch. |
    +
    +
    +
    +
    +
    +### ReturnLeaseResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | UNKNOWN should never be used. An internal LeaseService issue has happened if UNKNOWN is set. |
    +| STATUS_OK | 1 | ReturnLease was successful. |
    +| STATUS_INVALID_RESOURCE | 2 | ReturnLease failed because the resource covered by the lease is not being managed by the LeaseService. |
    +| STATUS_NOT_ACTIVE_LEASE | 3 | ReturnLease failed because the lease was not the active lease. |
    +| STATUS_NOT_AUTHORITATIVE_SERVICE | 4 | The LeaseService is not authoritative - so Acquire should not work. |
    +
    +
    +
    +
    +
    +### TakeLeaseResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | UNKNOWN should never be used. An internal LeaseService issue has happened if UNKNOWN is set. |
    +| STATUS_OK | 1 | TakeLease was successful. The lease field will be populated with the new lease for the resource. The client is expected to call the RetainLease method immediately after. |
    +| STATUS_INVALID_RESOURCE | 2 | TakeLease failed since the resource is not known to LeaseService. The ListLeaseResources method may be used to list all known resources. |
    +| STATUS_NOT_AUTHORITATIVE_SERVICE | 3 | The LeaseService is not authoritative - so Acquire should not work. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# lease_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### LeaseService
    +
    +LeaseService provides Leases of shared resources to clients.
    +An example of a shared resource is the set of leg motors on Spot, which
    +has the resource name of "body".
    +Clients can delegate out the Leases they receive from the LeaseService
    +to additional clients or services by generating sub-leases.
    +Leases obtained from the LeaseService may be revoked if the Lease holder
    +does not check in frequently to the LeaseService, or if another client
    +force-acquires a Lease.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| AcquireLease | [AcquireLeaseRequest](#bosdyn.api.AcquireLeaseRequest) | [AcquireLeaseResponse](#bosdyn.api.AcquireLeaseResponse) | Acquire a lease to a specific resource if the resource is available. |
    +| TakeLease | [TakeLeaseRequest](#bosdyn.api.TakeLeaseRequest) | [TakeLeaseResponse](#bosdyn.api.TakeLeaseResponse) | Take a lease for a specific resource even if another client has a lease. |
    +| ReturnLease | [ReturnLeaseRequest](#bosdyn.api.ReturnLeaseRequest) | [ReturnLeaseResponse](#bosdyn.api.ReturnLeaseResponse) | Return a lease to the LeaseService. |
    +| ListLeases | [ListLeasesRequest](#bosdyn.api.ListLeasesRequest) | [ListLeasesResponse](#bosdyn.api.ListLeasesResponse) | List state of all leases managed by the LeaseService. |
    +| RetainLease | [RetainLeaseRequest](#bosdyn.api.RetainLeaseRequest) | [RetainLeaseResponse](#bosdyn.api.RetainLeaseResponse) | Retain possession of a lease. |
    +
    + 
    +
    +
    +
    +
    +
    +# license.proto
    +
    +
    +
    +
    +
    +### GetFeatureEnabledRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| feature_codes | [string](#string) | Check if specific named features are enabled on the robot under the currently loaded license. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetFeatureEnabledResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| feature_enabled | [GetFeatureEnabledResponse.FeatureEnabledEntry](#bosdyn.api.GetFeatureEnabledResponse.FeatureEnabledEntry) | The resulting map showing the feature name (as the map key) and a boolean indicating if the feature is enabled with this license (as the map value). |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetFeatureEnabledResponse.FeatureEnabledEntry
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| key | [string](#string) |  |
    +| value | [bool](#bool) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetLicenseInfoRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetLicenseInfoResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header |
    +| license | [LicenseInfo](#bosdyn.api.LicenseInfo) | The details about the current license that is uploaded to the robot. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### LicenseInfo
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| status | [LicenseInfo.Status](#bosdyn.api.LicenseInfo.Status) | The status of the uploaded license for this robot. |
    +| id | [string](#string) | Unique license number. |
    +| robot_serial | [string](#string) | Serial number of the robot this license covers. |
    +| not_valid_before | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | The license is not valid for use for any dates before this timestamp. |
    +| not_valid_after | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | The license is not valid for use for any dates after this timestamp. |
    +| licensed_features | [string](#string) | Human readable list of licensed features included for this license. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### LicenseInfo.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 |  |
    +| STATUS_VALID | 1 |  |
    +| STATUS_EXPIRED | 2 |  |
    +| STATUS_NOT_YET_VALID | 3 |  |
    +| STATUS_MALFORMED | 4 |  |
    +| STATUS_SERIAL_MISMATCH | 5 |  |
    +| STATUS_NO_LICENSE | 6 |  |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# license_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### LicenseService
    +
    +The LicenseService allows clients to query the currently installed license on robot.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| GetLicenseInfo | [GetLicenseInfoRequest](#bosdyn.api.GetLicenseInfoRequest) | [GetLicenseInfoResponse](#bosdyn.api.GetLicenseInfoResponse) | Get information, such as the license number, dates of validity, and features for the license currently uploaded on the robot. |
    +| GetFeatureEnabled | [GetFeatureEnabledRequest](#bosdyn.api.GetFeatureEnabledRequest) | [GetFeatureEnabledResponse](#bosdyn.api.GetFeatureEnabledResponse) | Check if specific features (identified by string names) are enabled under the currently loaded license for this robot. |
    +
    + 
    +
    +
    +
    +
    +
    +# local_grid.proto
    +
    +
    +
    +
    +
    +### GetLocalGridTypesRequest
    +
    +The GetLocalGridTypes request message asks to the local grid types.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetLocalGridTypesResponse
    +
    +The GetLocalGridTypes response message returns to get all known string names for local grid types.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| local_grid_type | [LocalGridType](#bosdyn.api.LocalGridType) | The list of available local grid types. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetLocalGridsRequest
    +
    +The GetLocalGrid request message can request for multiple different types of local grids at one time.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| local_grid_requests | [LocalGridRequest](#bosdyn.api.LocalGridRequest) | Specifications of the requested local grids. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetLocalGridsResponse
    +
    +The GetLocalGrid response message replies with all of the local grid data for the requested types, and
    +a numerical count representing the amount of status errors that occurred when getting this data.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| local_grid_responses | [LocalGridResponse](#bosdyn.api.LocalGridResponse) | Response of local grid or error status for each requested local grid. |
    +| num_local_grid_errors | [int32](#int32) | The number of individual local grids requests which could not be satisfied. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### LocalGrid
    +
    +A grid-based local grid structure, which can represent different kinds of data, such as terrain
    +or obstacle data.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| local_grid_type_name | [string](#string) | The human readable string name that is used to identify the type of local grid data. |
    +| acquisition_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | The time at which the local grid data was computed and last valid at. |
    +| transforms_snapshot | [FrameTreeSnapshot](#bosdyn.api.FrameTreeSnapshot) | A tree-based collection of transformations, which will include the transformations to each of the returned local grids in addition to transformations to the common frames ("vision", "body", "odom"). All transforms within the snapshot are at the acquistion time of the local grid. |
    +| frame_name_local_grid_data | [string](#string) | The frame name for the local grid data. This frame refers to the corner of cell (0, 0), such that the map data is in the +x, +y quadrant. The cell data is packed in x-y order, so the cell at: data[xi + extent.num_cells_x * yj] has its center at position: {(xi + 0.5) * extent.cell_size, (yj + 0.5) * extent.cell_size}. |
    +| extent | [LocalGridExtent](#bosdyn.api.LocalGridExtent) | Location, size and resolution of the local grid. |
    +| cell_format | [LocalGrid.CellFormat](#bosdyn.api.LocalGrid.CellFormat) | The data type of all individual cells in the local grid. |
    +| encoding | [LocalGrid.Encoding](#bosdyn.api.LocalGrid.Encoding) | The encoding for the 'data' field of the local grid message. |
    +| data | [bytes](#bytes) | The encoded local grid representation. Cells are encoded according to the encoding enum, and are stored in in row-major order (x-major). This means that the data field has data entered row by row. The grid cell located at (i, j) will be at the (index = i * num_cells_x + j) within the data array. |
    +| rle_counts | [int32](#int32) | RLE pixel repetition counts: use data[i] repeated rle_counts[i] times when decoding the bytes data field. |
    +| cell_value_scale | [double](#double) | The scale for the cell value data; only valid if it is a non-zero number. |
    +| cell_value_offset | [double](#double) | A fixed value offset that is applied to each value of the cell data. Actual values in local grid are: (({value from data} * cell_value_scale) + cell_value_offset). |
    +
    +
    +
    +
    +
    +
    +
    +
    +### LocalGridExtent
    +
    +Information about the dimensions of the local grid, including the number of grid cells and
    +the size of each cell.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| cell_size | [double](#double) | Size of each side of the individual cells in the local grid (in meters). The area of a grid cell will be (cell_size x cell_size). |
    +| num_cells_x | [int32](#int32) | Number of cells along x extent of local grid (number of columns in local grid/ the local grid width). Note, that the (num_cells_x)x(num_cells_y) represents the total number of grid cells in the local grid. |
    +| num_cells_y | [int32](#int32) | Number of cells along y extent of local grid (number of rows in local grid). Note, that the (num_cells_x)x(num_cells_y) represents the totla number of grid cells in the local grid. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### LocalGridRequest
    +
    +LocalGrids are requested by LocalGridType string name.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| local_grid_type_name | [string](#string) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### LocalGridResponse
    +
    +The local grid response message will contain either the local grid or an error status.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| local_grid_type_name | [string](#string) | The type name of the local grid included in this response. |
    +| status | [LocalGridResponse.Status](#bosdyn.api.LocalGridResponse.Status) | Status of the request for the individual local grid. |
    +| local_grid | [LocalGrid](#bosdyn.api.LocalGrid) | The requested local grid data. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### LocalGridType
    +
    +Representation of an available type of local grid.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| name | [string](#string) |  |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### LocalGrid.CellFormat
    +
    +Describes the data type of a cell.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| CELL_FORMAT_UNKNOWN | 0 | Not specified -- not a valid value. |
    +| CELL_FORMAT_FLOAT32 | 1 | Each cell of the local grid is encoded as a little-endian 32-bit floating point number. |
    +| CELL_FORMAT_FLOAT64 | 2 | Each cell of the local grid is encoded as a little-endian 64-bit floating point number. |
    +| CELL_FORMAT_INT8 | 3 | Each cell of the local grid is encoded as a signed 8-bit integer. |
    +| CELL_FORMAT_UINT8 | 4 | Each cell of the local grid is encoded as an unsigned 8-bit integer. |
    +| CELL_FORMAT_INT16 | 5 | Each cell of the local grid is encoded as a little-endian signed 16-bit integer. |
    +| CELL_FORMAT_UINT16 | 6 | Each cell of the local grid is encoded as a little-endian unsigned 16-bit integer. |
    +
    +
    +
    +
    +
    +### LocalGrid.Encoding
    +
    +Encoding used for storing the local grid.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| ENCODING_UNKNOWN | 0 | Not specified -- not a valid value. |
    +| ENCODING_RAW | 1 | Cells are stored packed uncompressed. |
    +| ENCODING_RLE | 2 | Run-length encoding: repeat counts stored in rle_counts. |
    +
    +
    +
    +
    +
    +### LocalGridResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Not specified -- not a valid value. |
    +| STATUS_OK | 1 | LocalGrid was returned successfully. |
    +| STATUS_NO_SUCH_GRID | 2 | The requested local grid-type is unknown. |
    +| STATUS_DATA_UNAVAILABLE | 3 | The request local grid data is not available at this time. |
    +| STATUS_DATA_INVALID | 4 | The local grid data was not valid for some reason. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# local_grid_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### LocalGridService
    +
    +The map service provides access multiple kinds of cell-based map data.
    +It supports querying for the list of available types of local grids provided by the service,
    +and supports requesting a set of the latest local grids by map type name.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| GetLocalGridTypes | [GetLocalGridTypesRequest](#bosdyn.api.GetLocalGridTypesRequest) | [GetLocalGridTypesResponse](#bosdyn.api.GetLocalGridTypesResponse) | Obtain the list of available map types. The name field keys access to individual local grids when calling GetLocalGrids. |
    +| GetLocalGrids | [GetLocalGridsRequest](#bosdyn.api.GetLocalGridsRequest) | [GetLocalGridsResponse](#bosdyn.api.GetLocalGridsResponse) | Request a set of local grids by type name. |
    +
    + 
    +
    +
    +
    +
    +
    +# log_annotation.proto
    +
    +
    +
    +
    +
    +### AddLogAnnotationRequest
    +
    +DEPRECATED as of 2.1.0: Please use the DataBufferService instead of the LogAnnotationService.
    +The AddLogAnnotation request sends the information that should be added into the log.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request/response header. |
    +| annotations | [LogAnnotations](#bosdyn.api.LogAnnotations) | The annotations to be aded into the log (can be text messages, blobs or robot operator messages). |
    +
    +
    +
    +
    +
    +
    +
    +
    +### AddLogAnnotationResponse
    +
    +DEPRECATED as of 2.1.0: Please use the DataBufferService instead of the LogAnnotationService.
    +The AddLogAnnotation response message, which is empty except for any potential header errors/warnings.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common request/response header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### LogAnnotationLogBlob
    +
    +DEPRECATED as of 2.1.0: Please use the DataBufferService instead of the LogAnnotationService.
    +A unit of binary data to be entered in a log.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Required timestamp of data in robot clock time. |
    +| channel | [string](#string) | A general label for this blob. This is distinct from type_id, which identifies how the blob is to be parsed. |
    +| type_id | [string](#string) | A description of the data's content and its encoding. This should be sufficient for deciding how to deserialize the data. For example, this could be the full name of a protobuf message type. |
    +| data | [bytes](#bytes) | Raw data to be included as the blob log. |
    +| timestamp_client | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Optional timestamp of data in client clock time. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### LogAnnotationOperatorMessage
    +
    +DEPRECATED as of 2.1.0: Please use the DataBufferService instead of the LogAnnotationService.
    +An operator message to be added to the robot's logs.
    +These are notes especially intended to mark when logs should be preserved and reviewed
    +to ensure that robot hardware and/or software is working as intended.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| message | [string](#string) | String annotation message to add to the log. |
    +| timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Required timestamp of data in robot clock time. |
    +| timestamp_client | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Optional timestamp of data in client clock time. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### LogAnnotationTextMessage
    +
    +DEPRECATED as of 2.1.0: Please use the DataBufferService instead of the LogAnnotationService.
    +A text message to add to the robot's logs.
    +These could be internal text-log messages from a client for use in debugging, for
    +example.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| message | [string](#string) | String annotation message to add to the log. |
    +| timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Required timestamp of data in robot clock time. |
    +| service | [string](#string) | The service responsible for the annotation. May be omitted. |
    +| level | [LogAnnotationTextMessage.Level](#bosdyn.api.LogAnnotationTextMessage.Level) | Level of significance of the text message. |
    +| tag | [string](#string) | Optional tag to identify from what code/module this message originated from. |
    +| filename | [string](#string) | Optional source file name originating the log message. |
    +| line_number | [int32](#int32) | Optional source file line number originating the log message. |
    +| timestamp_client | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Optional timestamp of data in client clock time. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### LogAnnotations
    +
    +DEPRECATED as of 2.1.0: Please use the DataBufferService instead of the LogAnnotationService.
    +A container for elements to be added to the robot's logs.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| text_messages | [LogAnnotationTextMessage](#bosdyn.api.LogAnnotationTextMessage) | Text messages to be added to the log. |
    +| operator_messages | [LogAnnotationOperatorMessage](#bosdyn.api.LogAnnotationOperatorMessage) | Messages from the robot operator to be added to the log. |
    +| blob_data | [LogAnnotationLogBlob](#bosdyn.api.LogAnnotationLogBlob) | One or more binary blobs to add to the log. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### LogAnnotationTextMessage.Level
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| LEVEL_UNKNOWN | 0 | Invalid, do not use. |
    +| LEVEL_DEBUG | 1 | Events likely of interest only in a debugging context. |
    +| LEVEL_INFO | 2 | Informational message during normal operation. |
    +| LEVEL_WARN | 3 | Information about an unexpected but recoverable condition. |
    +| LEVEL_ERROR | 4 | Information about an operation which did not succeed. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# log_annotation_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### LogAnnotationService
    +
    +DEPRECATED as of 2.1.0: Please use the DataBufferService instead of the LogAnnotationService.
    +The LogAnnotationService is deprecated in release 2.1 and may be removed in the
    +future.
    +LogAnnotationService allows adding information to the robot's log files.
    +This service is a mechanism for adding information to the robot's log files.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| AddLogAnnotation | [AddLogAnnotationRequest](#bosdyn.api.AddLogAnnotationRequest) | [AddLogAnnotationResponse](#bosdyn.api.AddLogAnnotationResponse) | Add the specified information to the robot's log files. |
    +
    + 
    +
    +
    +
    +
    +
    +# manipulation_api.proto
    +
    +
    +
    +
    +
    +### AllowableOrientation
    +
    +Allowable orientation allow you to specify vectors that the different axes of the robot's
    +gripper will be aligned with in the final grasp pose. \
    +
    +Frame: \
    + In stow position, +X is to the front of the gripper, pointing forward. \
    +                   +Y is out of the side of the gripper going to the robot's left \
    +                   +Z is straight up towards the sky \
    +
    +Here, you can supply vectors that you want the gripper to be aligned with at the final grasp
    +position.  For example, if you wanted to grasp a cup, you'd wouldn't want a top-down grasp.
    +So you might specify: \
    +     frame_name = "vision" (so that Z is gravity aligned) \
    +      VectorAlignmentWithTolerance: \
    +         axis_to_on_gripper_ewrt_gripper = Vec3(0, 0, 1)  <--- we want to control the
    +                                                               gripper's z-axis. \
    +
    +         axis_to_align_with_ewrt_frame = Vec3(0, 0, 1)  <--- ...and we want that axis to be
    +                                                                straight up \
    +         tolerance_z = 0.52  <--- 30 degrees \
    +   This will ensure that the z-axis of the gripper is pointed within 30 degrees of vertical
    +   so that your grasp won't be top-down (which would need the z-axis of the gripper to be
    +   pointed at the horizon). \
    +
    +You can also specify more than one AllowableOrientation to give the system multiple options.
    +For example, you could specify that you're OK with either a z-up or z-down version of the cup
    +grasp, allowing the gripper roll 180 from the stow position to grasp the cup.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| rotation_with_tolerance | [RotationWithTolerance](#bosdyn.api.RotationWithTolerance) |  |
    +| vector_alignment_with_tolerance | [VectorAlignmentWithTolerance](#bosdyn.api.VectorAlignmentWithTolerance) |  |
    +| squeeze_grasp | [SqueezeGrasp](#bosdyn.api.SqueezeGrasp) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ApiGraspOverride
    +
    +Use this message to assert the ground truth about grasping.
    +Grasping is usually detected automatically by the robot. If the client wishes to override the
    +robot's determination of grasp status, send an ApiGraspOverride message with either:
    +OVERRIDE_HOLDING, indicating the gripper is holding something, or
    +OVERRIDE_NOT_HOLDING, indicating the gripper is not holding
    +anything.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| override_request | [ApiGraspOverride.Override](#bosdyn.api.ApiGraspOverride.Override) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ApiGraspOverrideRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| api_grasp_override | [ApiGraspOverride](#bosdyn.api.ApiGraspOverride) |  |
    +| carry_state_override | [ApiGraspedCarryStateOverride](#bosdyn.api.ApiGraspedCarryStateOverride) | If the grasp override is set to NOT_HOLDING, setting a carry_state_override message will cause the request to be rejected as malformed. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ApiGraspOverrideResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ApiGraspedCarryStateOverride
    +
    +Use this message to assert properties about the grasped item.
    +By default, the robot will assume all grasped items are not carriable.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| override_request | [ManipulatorState.CarryState](#bosdyn.api.ManipulatorState.CarryState) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GraspParams
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| grasp_palm_to_fingertip | [float](#float) | Where the grasp is on the hand. Set to 0 to be a (default) palm grasp, where the object will be pressed against the gripper's palm plate. Set to 1.0 to be a fingertip grasp, where the robot will try to pick up the target with just the tip of its fingers. \ Intermediate values move the grasp location between the two extremes. |
    +| grasp_params_frame_name | [string](#string) | Frame name for the frame that the constraints in allowable_orientation are expressed in. |
    +| allowable_orientation | [AllowableOrientation](#bosdyn.api.AllowableOrientation) | Optional constraints about the orientation of the grasp. This field lets you specify things like "only do a top down grasp," "grasp only from this direction," or "grasp with the gripper upside-down." If you don't pass anything, the robot will automatically search for a good grasp orientation. |
    +| position_constraint | [GraspPositionConstraint](#bosdyn.api.GraspPositionConstraint) | Optional parameter on how much the robot is allowed to move the grasp from where the user requested. Set this to be GRASP_POSITION_CONSTRAINT_FIXED_AT_USER_POSITION to get a grasp that is at the exact position you requested, but has less or no automatic grasp selection help in position. |
    +| manipulation_camera_source | [ManipulationCameraSource](#bosdyn.api.ManipulationCameraSource) | Optional hint about which camera was used to generate the target points. The robot will attempt to correct for calibration error between the arm and the body cameras. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ManipulationApiFeedbackRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| manipulation_cmd_id | [int32](#int32) | Unique identifier for the command, provided by ManipulationApiResponse. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ManipulationApiFeedbackResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| manipulation_cmd_id | [int32](#int32) | The unique identifier for the ManipulationApiFeedbackRequest. |
    +| current_state | [ManipulationFeedbackState](#bosdyn.api.ManipulationFeedbackState) |  |
    +| transforms_snapshot_manipulation_data | [FrameTreeSnapshot](#bosdyn.api.FrameTreeSnapshot) | Data from the manipulation system: \ "walkto_raycast_intersection": \ If you sent a WalkToObject request, we raycast in the world to intersect your pixel/ray with the world. The point of intersection is included in this transform snapshot with the name "walkto_raycast_intersection". \ "grasp_planning_solution": \ If you requested a grasp plan, this frame will contain the planning solution if available. This will be the pose of the "hand" frame at the completion of the grasp. \ "gripper_nearest_object": \ If the range camera in the hand senses an object, this frame will have the position of the nearest object. This is useful for getting a ballpark range measurement. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ManipulationApiRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| lease | [Lease](#bosdyn.api.Lease) | The Lease to show ownership of the robot. |
    +| walk_to_object_ray_in_world | [WalkToObjectRayInWorld](#bosdyn.api.WalkToObjectRayInWorld) | Walk to an object with a raycast in to the world |
    +| walk_to_object_in_image | [WalkToObjectInImage](#bosdyn.api.WalkToObjectInImage) | Walk to an object at a pixel location in an image. |
    +| pick_object | [PickObject](#bosdyn.api.PickObject) | Pick up an object. |
    +| pick_object_in_image | [PickObjectInImage](#bosdyn.api.PickObjectInImage) | Pick up an object at a pixel location in an image. |
    +| pick_object_ray_in_world | [PickObjectRayInWorld](#bosdyn.api.PickObjectRayInWorld) | Pick up an object based on a ray in 3D space. This is the lowest-level, most configurable object picking command. |
    +| pick_object_execute_plan | [PickObjectExecutePlan](#bosdyn.api.PickObjectExecutePlan) | Execute a previously planned pick. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ManipulationApiResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| manipulation_cmd_id | [int32](#int32) | ID of the manipulation command either just issued or that we are providing feedback for. |
    +| lease_use_result | [LeaseUseResult](#bosdyn.api.LeaseUseResult) | Details about how the lease was used. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PickObject
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| frame_name | [string](#string) | Name of the frame you want to give your input in. |
    +| object_rt_frame | [Vec3](#bosdyn.api.Vec3) | Pickup an object at the location, given in the frame named above. |
    +| grasp_params | [GraspParams](#bosdyn.api.GraspParams) | Optional parameters for the grasp. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PickObjectExecutePlan
    +
    +No data
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### PickObjectInImage
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| pixel_xy | [Vec2](#bosdyn.api.Vec2) | Pickup an object that is at a pixel location in an image. |
    +| transforms_snapshot_for_camera | [FrameTreeSnapshot](#bosdyn.api.FrameTreeSnapshot) | A tree-based collection of transformations, which will include the transformations to each image's sensor in addition to transformations to the common frames ("vision", "body", "odom"). All transforms within the snapshot are at the acquistion time of the image. |
    +| frame_name_image_sensor | [string](#string) | The frame name for the image's sensor source. This must be included in the transform snapshot. |
    +| camera_model | [ImageSource.PinholeModel](#bosdyn.api.ImageSource.PinholeModel) | Camera model. |
    +| grasp_params | [GraspParams](#bosdyn.api.GraspParams) | Optional parameters for the grasp. |
    +| walk_gaze_mode | [WalkGazeMode](#bosdyn.api.WalkGazeMode) | Automatic walking / gazing configuration. See detailed comment in the PickObjectRayInWorld message. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PickObjectRayInWorld
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| ray_start_rt_frame | [Vec3](#bosdyn.api.Vec3) | Cast a ray in the world and pick the first object found along the ray. \ This is the lowest-level grasping message; all other grasp options internally use this message to trigger a grasp. \ Example: You see the object you are interested in with the gripper's camera. To grasp it, you cast a ray from the camera out to 4 meters (well past the object). \ To do this you'd set: \ ray_start_rt_frame: camera's position \ ray_end_rt_frame: camera's position + unit vector along ray of interest * 4 meters |
    +| ray_end_rt_frame | [Vec3](#bosdyn.api.Vec3) |  |
    +| frame_name | [string](#string) | Name of the frame the above parameters are represented in. |
    +| grasp_params | [GraspParams](#bosdyn.api.GraspParams) | Optional parameters for the grasp. |
    +| walk_gaze_mode | [WalkGazeMode](#bosdyn.api.WalkGazeMode) | Configure if the robot should automatically walk and/or gaze at the target object before performing the grasp. \ 1. If you haven't moved the robot or deployed the arm, use PICK_AUTO_WALK_AND_GAZE \ 2. If you have moved to the location you want to pick from, but haven't yet deployed the arm, use PICK_AUTO_GAZE. \ 3. If you have already moved the robot and have the hand looking at your target object, use PICK_NO_AUTO_WALK_OR_GAZE. \ If you are seeing issues with "MANIP_STATE_GRASP_FAILED_TO_RAYCAST_INTO_MAP," that means that the automatic system cannot find your object when trying to automatically walk to it, so consider using PICK_AUTO_GAZE or PICK_NO_AUTO_WALK_OR_GAZE. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RotationWithTolerance
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| rotation_ewrt_frame | [Quaternion](#bosdyn.api.Quaternion) |  |
    +| threshold_radians | [float](#float) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SqueezeGrasp
    +
    +A "squeeze grasp" is a top-down grasp where we try to keep both jaws of the gripper in
    +contact with the ground and bring the jaws together.  This can allow the robot to pick up
    +small objects on the ground.
    +
    +If you specify a SqueezeGrasp as:
    +     allowed:
    +         - with no other allowable orientations:
    +             then the robot will perform a squeeze grasp.
    +         - with at least one other allowable orientation:
    +             the robot will attempt to find a normal grasp with that orientation and if it
    +             fails, will perform a squeeze grasp.
    +     disallowed:
    +         - with no other allowable orientations:
    +             the robot will perform an unconstrained grasp search and a grasp if a good grasp
    +             is found.  If no grasp is found, the robot will report
    +             MANIP_STATE_GRASP_PLANNING_NO_SOLUTION
    +         - with other allowable orientations:
    +             the robot will attempt to find a valid grasp.  If it cannot it will report
    +             MANIP_STATE_GRASP_PLANNING_NO_SOLUTION
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| squeeze_grasp_disallowed | [bool](#bool) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### VectorAlignmentWithTolerance
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| axis_on_gripper_ewrt_gripper | [Vec3](#bosdyn.api.Vec3) | Axis on the gripper that you want to align. For example, to align the front of the gripper to be straight down, you'd use: \ axis_on_gripper_ewrt_gripper = Vec3(1, 0, 0) \ axis_to_align_with_ewrt_frame = Vec3(0, 0, -1) (in the "vision" frame) \ |
    +| axis_to_align_with_ewrt_frame | [Vec3](#bosdyn.api.Vec3) |  |
    +| threshold_radians | [float](#float) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### WalkToObjectInImage
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| pixel_xy | [Vec2](#bosdyn.api.Vec2) | Walk to an object that is at a pixel location in an image. |
    +| transforms_snapshot_for_camera | [FrameTreeSnapshot](#bosdyn.api.FrameTreeSnapshot) | A tree-based collection of transformations, which will include the transformations to each image's sensor in addition to transformations to the common frames ("vision", "body", "odom"). All transforms within the snapshot are at the acquistion time of the image. |
    +| frame_name_image_sensor | [string](#string) | The frame name for the image's sensor source. This will be included in the transform snapshot. |
    +| camera_model | [ImageSource.PinholeModel](#bosdyn.api.ImageSource.PinholeModel) | Camera model. |
    +| offset_distance | [google.protobuf.FloatValue](#google.protobuf.FloatValue) | Optional offset distance for the robot to stand from the object's location. The robot will walk forwards or backwards from where it is so that its center of mass is this distance from the object. \ If unset, we use a reasonable default value. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### WalkToObjectRayInWorld
    +
    +Walks the robot up to an object.  Useful to prepare to grasp or manipulate something.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| ray_start_rt_frame | [Vec3](#bosdyn.api.Vec3) | Position of the start of the ray (see PickObjectRayInWorld for detailed comments.) |
    +| ray_end_rt_frame | [Vec3](#bosdyn.api.Vec3) | Position of the end of the ray. |
    +| frame_name | [string](#string) | Name of the frame that the above parameters are expressed in. |
    +| offset_distance | [google.protobuf.FloatValue](#google.protobuf.FloatValue) | Optional offset distance for the robot to stand from the object's location. The robot will walk forwards or backwards from where it is so that its center of mass is this distance from the object. \ If unset, we use a reasonable default value. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### ApiGraspOverride.Override
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| OVERRIDE_UNKNOWN | 0 |  |
    +| OVERRIDE_HOLDING | 1 |  |
    +| OVERRIDE_NOT_HOLDING | 2 |  |
    +
    +
    +
    +
    +
    +### GraspPositionConstraint
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| GRASP_POSITION_CONSTRAINT_UNKNOWN | 0 |  |
    +| GRASP_POSITION_CONSTRAINT_NORMAL | 1 |  |
    +| GRASP_POSITION_CONSTRAINT_FIXED_AT_USER_POSITION | 2 |  |
    +
    +
    +
    +
    +
    +### ManipulationCameraSource
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| MANIPULATION_CAMERA_SOURCE_UNKNOWN | 0 |  |
    +| MANIPULATION_CAMERA_SOURCE_STEREO | 1 |  |
    +| MANIPULATION_CAMERA_SOURCE_HAND | 2 |  |
    +
    +
    +
    +
    +
    +### ManipulationFeedbackState
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| MANIP_STATE_UNKNOWN | 0 |  |
    +| MANIP_STATE_DONE | 1 |  |
    +| MANIP_STATE_SEARCHING_FOR_GRASP | 2 |  |
    +| MANIP_STATE_MOVING_TO_GRASP | 3 |  |
    +| MANIP_STATE_GRASPING_OBJECT | 4 |  |
    +| MANIP_STATE_PLACING_OBJECT | 5 |  |
    +| MANIP_STATE_GRASP_SUCCEEDED | 6 |  |
    +| MANIP_STATE_GRASP_FAILED | 7 |  |
    +| MANIP_STATE_GRASP_PLANNING_SUCCEEDED | 11 |  |
    +| MANIP_STATE_GRASP_PLANNING_NO_SOLUTION | 8 |  |
    +| MANIP_STATE_GRASP_FAILED_TO_RAYCAST_INTO_MAP | 9 | Note: if you are experiencing raycast failures during grasping, consider using a different grasping call that does not require the robot to automatically walk up to the grasp. |
    +| MANIP_STATE_GRASP_PLANNING_WAITING_DATA_AT_EDGE | 13 | The grasp planner is waiting for the gaze to have the target object not on the edge of the camera view. If you are seeing this in an automatic mode, the robot will soon retarget the grasp for you. If you are seeing this in a non-auto mode, you'll need to change your gaze to have the target object more in the center of the hand-camera's view. |
    +| MANIP_STATE_WALKING_TO_OBJECT | 10 |  |
    +| MANIP_STATE_ATTEMPTING_RAYCASTING | 12 |  |
    +| MANIP_STATE_MOVING_TO_PLACE | 14 |  |
    +| MANIP_STATE_PLACE_FAILED_TO_RAYCAST_INTO_MAP | 15 |  |
    +| MANIP_STATE_PLACE_SUCCEEDED | 16 |  |
    +| MANIP_STATE_PLACE_FAILED | 17 |  |
    +
    +
    +
    +
    +
    +### WalkGazeMode
    +
    +Configure automatic walking and gazing at the target.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| PICK_WALK_GAZE_UNKNOWN | 0 |  |
    +| PICK_AUTO_WALK_AND_GAZE | 1 | Default, walk to the target and gaze at it automatically |
    +| PICK_AUTO_GAZE | 2 | Don't move the robot base, but automatically look at the grasp target. |
    +| PICK_NO_AUTO_WALK_OR_GAZE | 3 | No automatic gazing or walking. Note: if you choose this option, the target location must not be near the edges or off the screen on the hand camera's view. |
    +| PICK_PLAN_ONLY | 4 | Only plan for the grasp, don't move the robot. Since we won't move the robot, the target location must not be near the edges or out of the hand camera's view. The robot must be located near the object. (Equivalent conditions as for success with PICK_NO_AUTO_WALK_OR_GAZE) |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# manipulation_api_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### ManipulationApiService
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| ManipulationApi | [ManipulationApiRequest](#bosdyn.api.ManipulationApiRequest) | [ManipulationApiResponse](#bosdyn.api.ManipulationApiResponse) |  |
    +| ManipulationApiFeedback | [ManipulationApiFeedbackRequest](#bosdyn.api.ManipulationApiFeedbackRequest) | [ManipulationApiFeedbackResponse](#bosdyn.api.ManipulationApiFeedbackResponse) |  |
    +| OverrideGrasp | [ApiGraspOverrideRequest](#bosdyn.api.ApiGraspOverrideRequest) | [ApiGraspOverrideResponse](#bosdyn.api.ApiGraspOverrideResponse) |  |
    +
    + 
    +
    +
    +
    +
    +
    +# mission/mission.proto
    +
    +
    +
    +
    +
    +### AnswerQuestionRequest
    +
    +Answer one of the outstanding questions.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| question_id | [int64](#int64) | Identifier of the question being answered. |
    +| code | [int64](#int64) | The answer_code from the Question, corresponding to the user's choice. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### AnswerQuestionResponse
    +
    +Response from the server after a client has answered one of its questions.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [AnswerQuestionResponse.Status](#bosdyn.api.mission.AnswerQuestionResponse.Status) | The result of the AnswerQuestionRequest. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### FailedNode
    +
    +General message describing a node that has failed, for example as part of a PlayMission or
    +LoadMission RPC.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| name | [string](#string) | Human-readable name of this node, e.g. "Goto waypoint 1", or "Power On". |
    +| error | [string](#string) | The reason why this node failed. May not be provided by all nodes. |
    +| impl_typename | [string](#string) | The type of node, e.g. "bosdyn.api.mission.Sequence". May not be provided by all nodes. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetInfoRequest
    +
    +Request mission information.
    +This covers information that stays static until a new mission is loaded.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetInfoResponse
    +
    +Provides the currently loaded mission's information.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| mission_info | [MissionInfo](#bosdyn.api.mission.MissionInfo) | Description of the loaded mission's structure. Unset if no mission has been successfully loaded. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetMissionRequest
    +
    +For requesting the mission as it was loaded in LoadMission.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetMissionResponse
    +
    +Responding with the mission as it was loaded in LoadMission.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| root | [Node](#bosdyn.api.mission.Node) | Root node of the mission loaded. Unset if no mission has been loaded. |
    +| id | [int64](#int64) | Mission ID as reported in MissionInfo. -1 if no mission has been loaded. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetStateRequest
    +
    +Get the state of the mission.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| history_upper_tick_bound | [google.protobuf.Int64Value](#google.protobuf.Int64Value) | Upper bound on the node state to retrieve, inclusive. Leave unset for the latest data. |
    +| history_lower_tick_bound | [int64](#int64) | Tick counter for the lower bound of per-node state to retrieve. |
    +| history_past_ticks | [int64](#int64) | Number of ticks to look into the past from the upper bound. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetStateResponse
    +
    +Response to a GetStateRequest.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| state | [State](#bosdyn.api.mission.State) | The requested mission state. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### LoadMissionRequest
    +
    +The LoadMission request specifies a root node for the mission that should be used.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| root | [Node](#bosdyn.api.mission.Node) | Root node of the mission to load. |
    +| leases | [bosdyn.api.Lease](#bosdyn.api.Lease) | Leases that will be needed to validate the mission. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### LoadMissionResponse
    +
    +The LoadMission response returns the mission info generated by the service if successfully loaded, and
    +a status and other inforamtion if the request fails.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [LoadMissionResponse.Status](#bosdyn.api.mission.LoadMissionResponse.Status) | Result of loading the mission. |
    +| lease_use_results | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | Results from any leases that may have been used. As part of mission validation, some of the non-mission leases may have been used. |
    +| mission_info | [MissionInfo](#bosdyn.api.mission.MissionInfo) | Provides the structure of the mission. Set when loading succeeds. |
    +| failed_nodes | [FailedNode](#bosdyn.api.mission.FailedNode) | If certain nodes failed compilation or validation, they will be reported back in this field. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### MissionInfo
    +
    +Static information about the mission. Used to interpret the mission state.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| id | [int64](#int64) | Mission ID assigned by the server. |
    +| root | [NodeInfo](#bosdyn.api.mission.NodeInfo) | The root node of the mission. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### NodeInfo
    +
    +Provides children and metadata of a single node within the mission.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| id | [int64](#int64) | Unique to each node within the LOADED mission. Not guaranteed to be consistent between loads of the same mission. Used to identify the nodes in the State message. |
    +| name | [string](#string) | Human-readable name of this node, e.g. "Goto waypoint 1", or "Power On". |
    +| user_data | [UserData](#bosdyn.api.mission.UserData) | Any UserData that was associated with this node. |
    +| children | [NodeInfo](#bosdyn.api.mission.NodeInfo) | Info on all children of this node, if any are present. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PauseMissionRequest
    +
    +The PauseMission request message will pause the mission that is currently executing, if there is one.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| lease | [bosdyn.api.Lease](#bosdyn.api.Lease) | Lease on the mission service. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PauseMissionResponse
    +
    +The PauseMission response message will return the status of the request.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [PauseMissionResponse.Status](#bosdyn.api.mission.PauseMissionResponse.Status) | Result of the pause request. |
    +| lease_use_result | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | Result of the lease in the pause request. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PlayMissionRequest
    +
    +A request to play the currently loaded mission for a fixed amount of time.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| pause_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Run the mission until this time. Pause the mission at that time if we have not received a new PlayMissionRequest. This ensures the mission stops relatively quickly if there is an unexpected client drop-out. Clients should regularly send PlayMissionRequests with a pause_time that reflects how often they expect to check in with the mission service. |
    +| leases | [bosdyn.api.Lease](#bosdyn.api.Lease) | Leases that the mission will need, plus the lease on the mission service. |
    +| settings | [PlaySettings](#bosdyn.api.mission.PlaySettings) | Settings active until the next PlayMission or RestartMission request. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PlayMissionResponse
    +
    +The PlayMission response message will return the status of the play mission request.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [PlayMissionResponse.Status](#bosdyn.api.mission.PlayMissionResponse.Status) | The result of the play request. |
    +| lease_use_results | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | Results from any leases that may have been provided with the play request. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PlaySettings
    +
    +"Global" settings to use while a mission is running.
    +Some of these settings are not globally applicable. For example, the velocity_limit
    +does not change the speed at which the robot poses the body.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| velocity_limit | [bosdyn.api.SE2VelocityLimit](#bosdyn.api.SE2VelocityLimit) | Velocity limits on the robot motion. Example use: limit velocity in "navigate to" nodes. |
    +| disable_directed_exploration | [bool](#bool) | Disable directed exploration to bypass blocked path sections |
    +| disable_alternate_route_finding | [bool](#bool) | Disable alternate-route-finding; overrides the per-edge setting in the map. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Question
    +
    +A question posed by a Prompt node, or by the internal operation of another node.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| id | [int64](#int64) | Identifier of this question, unique across all missions executing on a single host. |
    +| source | [string](#string) | What's asking the question. Should be unique in the active mission. |
    +| text | [string](#string) | The text of the question itself. |
    +| options | [Prompt.Option](#bosdyn.api.mission.Prompt.Option) | Options to choose from. Uses the submessage from the "prompt" node message. |
    +| for_autonomous_processing | [bool](#bool) | Set to true if this question was meant to be answered by some automated system, not a human. Clients should usually avoid generating a UI element to ask such a question. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RestartMissionRequest
    +
    +A request to restart the currently loaded mission.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| pause_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Run the mission until this time. Pause the mission at that time if we have not received a new PlayMissionRequest. This ensures the mission stops relatively quickly if there is an unexpected client drop-out. Clients should regularly send PlayMissionRequests with a pause_time that reflects how often they expect to check in with the mission service. |
    +| leases | [bosdyn.api.Lease](#bosdyn.api.Lease) | Leases that the mission will need, plus the lease on the mission service. |
    +| settings | [PlaySettings](#bosdyn.api.mission.PlaySettings) | Settings active until the next PlayMission or RestartMission request. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RestartMissionResponse
    +
    +The RestartMission response includes the status and any failed nodes for the request.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [RestartMissionResponse.Status](#bosdyn.api.mission.RestartMissionResponse.Status) | The result of the restart request. |
    +| lease_use_results | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | Results from any leases that may have been used. As part of mission validation, some of the non-mission leases may have been used. |
    +| failed_nodes | [FailedNode](#bosdyn.api.mission.FailedNode) | If certain nodes failed validation, they will be reported back in this field. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### State
    +
    +State of the mission service.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| questions | [Question](#bosdyn.api.mission.Question) | What questions are outstanding? |
    +| answered_questions | [State.AnsweredQuestion](#bosdyn.api.mission.State.AnsweredQuestion) | History of questions that have been answered. The server will set some limit on the available history. |
    +| history | [State.NodeStatesAtTick](#bosdyn.api.mission.State.NodeStatesAtTick) | Node states ordered from newest to oldest. history[0] will always be the data from this tick. |
    +| status | [State.Status](#bosdyn.api.mission.State.Status) | Current status of the mission. |
    +| error | [string](#string) | Describes the unexpected error encountered by the mission service. Only filled out if STATUS_ERROR is set. |
    +| tick_counter | [int64](#int64) | The mission's tick counter when this state was generated. -1 indicates no mission has been started. |
    +| mission_id | [int64](#int64) | The mission's ID. -1 indicates no mission has been loaded. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### State.AnsweredQuestion
    +
    +A question that has been answered already.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| question | [Question](#bosdyn.api.mission.Question) | The question that this state information is related to. |
    +| accepted_answer_code | [int64](#int64) | The answer that was provided. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### State.NodeStatesAtTick
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| tick_counter | [int64](#int64) | The tick counter when this state was produced. |
    +| tick_start_timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Time at which this tick started, in host time basis. |
    +| node_states | [State.NodeStatesAtTick.NodeState](#bosdyn.api.mission.State.NodeStatesAtTick.NodeState) | At this tick, the state of every node that was ticked, in the order they were ticked. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### State.NodeStatesAtTick.NodeState
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| result | [Result](#bosdyn.api.mission.Result) | The result of this node's tick. |
    +| error | [string](#string) | May be set when the 'result' is RESULT_FAILURE or RESULT_ERROR, this describes why the node failed. Not all nodes will have an error explaining why they failed. |
    +| id | [int64](#int64) | ID from NodeInfo. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StopMissionRequest
    +
    +The StopMission request message will fully stop the mission.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| lease | [bosdyn.api.Lease](#bosdyn.api.Lease) | Lease on the mission service. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StopMissionResponse
    +
    +The StopMission response message will return the status of the request.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [StopMissionResponse.Status](#bosdyn.api.mission.StopMissionResponse.Status) | Result of the stop request. |
    +| lease_use_result | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | Result of the lease in the stop request. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### AnswerQuestionResponse.Status
    +
    +Possible results for answering a question.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Invalid; do not use. |
    +| STATUS_OK | 1 | Answer accepted. |
    +| STATUS_INVALID_QUESTION_ID | 2 | Question ID is not valid / unknown by the mission service. |
    +| STATUS_INVALID_CODE | 3 | Answer code is not applicable for the question indicated. |
    +| STATUS_ALREADY_ANSWERED | 4 | Question was already answered. |
    +
    +
    +
    +
    +
    +### LoadMissionResponse.Status
    +
    +Possible results of loading a mission.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Invalid status, do not use. |
    +| STATUS_OK | 1 | The mission was loaded successfully. |
    +| STATUS_COMPILE_ERROR | 2 | Load-time compilation failed. The mission was malformed. |
    +| STATUS_VALIDATE_ERROR | 3 | Load-time validation failed. Some part of the mission was unable to initialize. |
    +
    +
    +
    +
    +
    +### PauseMissionResponse.Status
    +
    +Possible results of a pause request.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Invalid status, do not use. |
    +| STATUS_OK | 1 | Mission is paused or finished running. |
    +| STATUS_NO_MISSION_PLAYING | 2 | No mission has started playing. NOT returned when two PauseMissionRequests are received back-to-back. In that case, you will get STATUS_OK. |
    +
    +
    +
    +
    +
    +### PlayMissionResponse.Status
    +
    +Possible results for a play request.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Invalid status, do not use. |
    +| STATUS_OK | 1 | Mission is playing, or the mission has already completed. Use GetStateResponse to tell the difference. |
    +| STATUS_NO_MISSION | 2 | Call LoadMission first. |
    +
    +
    +
    +
    +
    +### RestartMissionResponse.Status
    +
    +Possible results of requesting a restart.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Invalid status, do not use. |
    +| STATUS_OK | 1 | Mission has restarted. |
    +| STATUS_NO_MISSION | 2 | Call LoadMission first. |
    +| STATUS_VALIDATE_ERROR | 3 | Validation failed. |
    +
    +
    +
    +
    +
    +### State.Status
    +
    +Possible overall status states of the mission.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Invalid status, do not use. |
    +| STATUS_FAILURE | 1 | The mission has failed due to a node failure. |
    +| STATUS_RUNNING | 2 | The mission is still running. |
    +| STATUS_SUCCESS | 3 | The mission succeeded! |
    +| STATUS_PAUSED | 4 | Execution has been paused. |
    +| STATUS_ERROR | 5 | The mission service itself encountered an unexpected error, outside of a node failing. |
    +| STATUS_NONE | 6 | No mission has been loaded. |
    +| STATUS_STOPPED | 7 | The mission was stopped before completion. |
    +
    +
    +
    +
    +
    +### StopMissionResponse.Status
    +
    +Possible results of a stop request.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Invalid status, do not use. |
    +| STATUS_OK | 1 | Mission is stopped/complete. The mission state may be in any of the "complete states", e.g. if the mission completed successfully before this RPC took effect, the mission will report STATUS_SUCCESS and not STATUS_STOPPED. |
    +| STATUS_NO_MISSION_PLAYING | 2 | No mission has started playing. NOT returned if the mission is already stopped. In that case, you will get STATUS_OK. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# mission/mission_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### MissionService
    +
    +The MissionService can be used to specify high level autonomous behaviors for Spot using behavior trees.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| LoadMission | [LoadMissionRequest](#bosdyn.api.mission.LoadMissionRequest) | [LoadMissionResponse](#bosdyn.api.mission.LoadMissionResponse) | Load a mission to run later. |
    +| LoadMissionAsChunks | [.bosdyn.api.DataChunk](#bosdyn.api.DataChunk) stream | [LoadMissionResponse](#bosdyn.api.mission.LoadMissionResponse) | Alternative loading method for large missions, that allows you to break the mission up into multiple streamed requests. The data chunks should deserialize into a LoadMissionRequest |
    +| PlayMission | [PlayMissionRequest](#bosdyn.api.mission.PlayMissionRequest) | [PlayMissionResponse](#bosdyn.api.mission.PlayMissionResponse) | Start executing a loaded mission. Will not restart a mission that has run to completion. Use RestartMission to do that. |
    +| PauseMission | [PauseMissionRequest](#bosdyn.api.mission.PauseMissionRequest) | [PauseMissionResponse](#bosdyn.api.mission.PauseMissionResponse) | Pause mission execution. |
    +| StopMission | [StopMissionRequest](#bosdyn.api.mission.StopMissionRequest) | [StopMissionResponse](#bosdyn.api.mission.StopMissionResponse) | Stop a running mission. Must use RestartMission, not PlayMission, to begin from the beginning. |
    +| RestartMission | [RestartMissionRequest](#bosdyn.api.mission.RestartMissionRequest) | [RestartMissionResponse](#bosdyn.api.mission.RestartMissionResponse) | Start executing a loaded mission from the beginning. Does not need to be called after LoadMission. |
    +| GetState | [GetStateRequest](#bosdyn.api.mission.GetStateRequest) | [GetStateResponse](#bosdyn.api.mission.GetStateResponse) | Get the state of the mission. |
    +| GetInfo | [GetInfoRequest](#bosdyn.api.mission.GetInfoRequest) | [GetInfoResponse](#bosdyn.api.mission.GetInfoResponse) | Get static information regarding the mission. Used to interpret mission state. |
    +| GetMission | [GetMissionRequest](#bosdyn.api.mission.GetMissionRequest) | [GetMissionResponse](#bosdyn.api.mission.GetMissionResponse) | Download the mission as it was uploaded to the service. |
    +| AnswerQuestion | [AnswerQuestionRequest](#bosdyn.api.mission.AnswerQuestionRequest) | [AnswerQuestionResponse](#bosdyn.api.mission.AnswerQuestionResponse) | Specify an answer to the question asked by the mission. |
    +
    + 
    +
    +
    +
    +
    +
    +# mission/nodes.proto
    +
    +
    +
    +
    +
    +### BosdynDockState
    +
    +Get the state of the docking service from the robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| service_name | [string](#string) | Name of the service to use. |
    +| host | [string](#string) | Host machine the service is running on. |
    +| child | [Node](#bosdyn.api.mission.Node) | Child node. Children will have access to the state gathered by this node. |
    +| state_name | [string](#string) | Name of the bosdyn.api.DockState object in the blackboard. For example, if this is set to "power_status", children can look up "power_status" in the blackboard. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### BosdynGraphNavLocalize
    +
    +Tell GraphNav to re-localize the robot using a SetLocalizationRequest. This overrides whatever
    +the current localization is. This can be useful to reinitialize the system at a known state.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| service_name | [string](#string) | Name of the service to use. |
    +| host | [string](#string) | Host machine the service is running on. |
    +| localization_request | [bosdyn.api.graph_nav.SetLocalizationRequest](#bosdyn.api.graph_nav.SetLocalizationRequest) | If no localization_request is provided, the default options used are FIDUCIAL_INIT_NEAREST (the system will initialize to the nearest fiducial). Otherwise, the options inside the set_localization_request will be used. Note that ko_tform_body in the request will be ignored (it will be recalculated at runtime). |
    +
    +
    +
    +
    +
    +
    +
    +
    +### BosdynGraphNavState
    +
    +Get GraphNav state from the robot and save it to the blackboard.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| service_name | [string](#string) | Name of the service to use. |
    +| host | [string](#string) | Host machine the service is running on. |
    +| child | [Node](#bosdyn.api.mission.Node) | Child node. Children will have access to the state gathered by this node. |
    +| state_name | [string](#string) | Name of the bosdyn.api.GetLocalizationStateResponse object in the blackboard. For example, if this is set to "nav", children can look up "nav.localization.waypoint_id" in the blackboard to get the waypoint the robot is localized to. |
    +| waypoint_id | [string](#string) | Id of the waypoint that we want the localization to be relative to. If this is empty, the localization will be relative to the waypoint that the robot is currently localized to. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### BosdynNavigateRoute
    +
    +Tell the robot to navigate a route.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| service_name | [string](#string) | Name of the service to use. |
    +| host | [string](#string) | Host machine the service is running on. |
    +| route | [bosdyn.api.graph_nav.Route](#bosdyn.api.graph_nav.Route) | A route for the robot to follow. |
    +| route_follow_params | [bosdyn.api.graph_nav.RouteFollowingParams](#bosdyn.api.graph_nav.RouteFollowingParams) | What should the robot do if it is not at the expected point in the route, or the route is blocked. |
    +| travel_params | [bosdyn.api.graph_nav.TravelParams](#bosdyn.api.graph_nav.TravelParams) | Parameters that define how to traverse and end the route. |
    +| navigation_feedback_response_blackboard_key | [string](#string) | If provided, this will write the last NavigationFeedbackResponse message to a blackboard variable with this name. |
    +| navigate_route_response_blackboard_key | [string](#string) | If provided, this will write the last NavigateRouteResponse message to a blackboard variable with this name. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### BosdynNavigateTo
    +
    +Tell the robot to navigate to a waypoint.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| service_name | [string](#string) | Name of the service to use. |
    +| host | [string](#string) | Host machine the service is running on. |
    +| destination_waypoint_id | [string](#string) | ID of the waypoint to go to. |
    +| route_gen_params | [bosdyn.api.graph_nav.RouteGenParams](#bosdyn.api.graph_nav.RouteGenParams) | Preferences on how to pick the route. |
    +| travel_params | [bosdyn.api.graph_nav.TravelParams](#bosdyn.api.graph_nav.TravelParams) | Parameters that define how to traverse and end the route. |
    +| navigation_feedback_response_blackboard_key | [string](#string) | If provided, this will write the last NavigationFeedbackResponse message to a blackboard variable with this name. |
    +| navigate_to_response_blackboard_key | [string](#string) | If provided, this will write the last NavigateToResponse message to a blackboard variable with this name. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### BosdynPowerRequest
    +
    +Make a robot power request
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| service_name | [string](#string) | Name of the service to use. |
    +| host | [string](#string) | Host machine the service is running on. |
    +| request | [bosdyn.api.PowerCommandRequest.Request](#bosdyn.api.PowerCommandRequest.Request) | The request to make. See the PowerCommandRequest documentation for details. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### BosdynRecordEvent
    +
    +Record an APIEvent
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| service_name | [string](#string) | Name of the service to use. |
    +| host | [string](#string) | Host machine the service is running on. |
    +| event | [bosdyn.api.Event](#bosdyn.api.Event) | The event to be logged. Note that everything should be populated except the id, start_time and end_time. The start and end time will be populated by the mission, using the node's start time. The id field shouldn't be set when the start and end times are the same. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### BosdynRobotCommand
    +
    +Execute a RobotCommand.
    +These nodes will "succeed" once a feedback response is received indicating success. Any commands
    +that require an "end time" will have that information set based on the end time of the mission.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| service_name | [string](#string) | Name of the service to use. |
    +| host | [string](#string) | Host machine the directory is running on. |
    +| command | [bosdyn.api.RobotCommand](#bosdyn.api.RobotCommand) | The command to execute. See the RobotCommand documentation for details. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### BosdynRobotState
    +
    +Get state from the robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| service_name | [string](#string) | Name of the service to use. |
    +| host | [string](#string) | Host machine the service is running on. |
    +| child | [Node](#bosdyn.api.mission.Node) | Child node. Children will have access to the state gathered by this node. |
    +| state_name | [string](#string) | Name of the bosdyn.api.RobotState object in the blackboard. For example, if this is set to "robot", children can look up "robot.power_state.motor_power_state" in the blackboard. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Condition
    +
    +Checks a simple comparison statement.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| lhs | [Condition.Operand](#bosdyn.api.mission.Condition.Operand) | Left-hand side of the comparison. |
    +| rhs | [Condition.Operand](#bosdyn.api.mission.Condition.Operand) | Right-hand side of the comparison. |
    +| operation | [Condition.Compare](#bosdyn.api.mission.Condition.Compare) | Comparison operator to compare lhs and rhs. |
    +| handle_staleness | [Condition.HandleStaleness](#bosdyn.api.mission.Condition.HandleStaleness) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Condition.Operand
    +
    +Options for where to retrieve values from.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| var | [VariableDeclaration](#bosdyn.api.mission.VariableDeclaration) | Reference an existing variable. |
    +| const | [ConstantValue](#bosdyn.api.mission.ConstantValue) | Use a constant value. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ConstantResult
    +
    +Just returns a constant when calling tick().
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| result | [Result](#bosdyn.api.mission.Result) | This result is always returned when calling tick(). |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DataAcquisition
    +
    +Trigger the acquisition and storage of data.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| service_name | [string](#string) | Name of the service to use. |
    +| host | [string](#string) | Host machine of the directory server that the data acquisition service is registered with. |
    +| request | [bosdyn.api.AcquireDataRequest](#bosdyn.api.AcquireDataRequest) | Specification of the data and metadata to store. |
    +| completion_behavior | [DataAcquisition.CompletionBehavior](#bosdyn.api.mission.DataAcquisition.CompletionBehavior) |  |
    +| group_name_format | [string](#string) | Define a format string that will be used together with the blackboard to generate a group name. Values from the blackboard will replace the keys in braces {}. Example: "telop-{date}", where "date" is a blackboard variable. Example: "{date}_loop_{loop_counter}", where "loop_counter" is a blackboard variable from a Repeat. |
    +| request_name_in_blackboard | [string](#string) | If populated, name of the variable in the blackboard in which to store the AcquireDataRequest. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DateToBlackboard
    +
    +Record a datetime string into the blackboard. Writes the date according to ISO8601 format.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| key | [string](#string) | The key of the variable that will be written. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DefineBlackboard
    +
    +Defines new blackboard variables within the scope of the child. Shadows blackboard
    +variables in the parent scope.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| blackboard_variables | [KeyValue](#bosdyn.api.mission.KeyValue) | The list of variables that should be defined for this subtree, with initial values. |
    +| child | [Node](#bosdyn.api.mission.Node) | The blackboard variables will only persist in the subtree defined by this child node. The child's tick() will be called on the child until it returns either SUCCESS or FAILURE. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Dock
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| service_name | [string](#string) | Name of the service to use. |
    +| host | [string](#string) | Host machine of the directory server that the docking service is registered with. |
    +| docking_station_id | [uint32](#uint32) | ID of docking station to dock at. |
    +| child | [Node](#bosdyn.api.mission.Node) | Optional child node. Children will have access to the status variables gathered by this node. If specified, child node will determine success/failure of this node.
    +
    +DEPRECATED! Use docking_command_response_blackboard_key and docking_command_feedback_response_blackboard_key instead. |
    +| command_status_name | [string](#string) | Name of the command status variable in the blackboard. This is the status of the docking command request made to the robot. Please refer to bosdyn.api.docking.DockingCommandResponse.Status for more details. Children can use this name to look up docking command status in the blackboard. If no name is provided, status will not be available.
    +
    +DEPRECATED! Use docking_command_response_blackboard_key and docking_command_feedback_response_blackboard_key instead. |
    +| feedback_status_name | [string](#string) | Name of the feedback status variable in the blackboard. This is the feedback provided while docking is in progress. Please refer to bosdyn.api.docking.DockingCommandFeedbackResponse.Status for a list of possible status values. Children can use this name to look up docking status in the blackboard. If no name is provided, status will not be available.
    +
    +DEPRECATED! Use docking_command_response_blackboard_key and docking_command_feedback_response_blackboard_key instead. |
    +| prep_pose_behavior | [bosdyn.api.docking.PrepPoseBehavior](#bosdyn.api.docking.PrepPoseBehavior) | Defines how we use the "pre-docking" behavior. |
    +| docking_command_feedback_response_blackboard_key | [string](#string) | If provided, this will write the last DockingCommandFeedbackResponse message to a blackboard variable with this name. |
    +| docking_command_response_blackboard_key | [string](#string) | If provided, this will write the last DockingCommandResponse message to a blackboard variable with this name. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ForDuration
    +
    +Run this child for a maximum amount of mission execution time.
    +Will exit with child's status if the child finishes early,
    +FAILURE if the child remains in RUNNING state for too long 
    +and no timeout_child is specified, or the status of the
    +timeout_child.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| duration | [google.protobuf.Duration](#google.protobuf.Duration) | Maximum duration of mission execution time. |
    +| child | [Node](#bosdyn.api.mission.Node) | Child to execute for the duration. |
    +| time_remaining_name | [string](#string) | Optional blackboard variable name. If specified, this node will define a blackboard variable that its child has access to, and write the number of seconds remaining as a double to the blackboard under this name. |
    +| timeout_child | [Node](#bosdyn.api.mission.Node) | Optional node that will run if the child times out. If not specified, this node will return FAILURE when the child times out. If specified, and the child times out, this node will return the status of the timeout_child. The timeout_child does not respect the original timeout. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### FormatBlackboard
    +
    +Sets a blackboard variable to a formatted string, reading from other blackboard vars.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| key | [string](#string) | The key of the variable that will be written. |
    +| format | [string](#string) | Define a format string that will be used together with the blackboard to generate string value. Values from the blackboard will replace the keys in braces {}, i.e. {blackboard_variable_name}. We also allow some string formatting options, namely:
    +
    +1) Floating point decimal places: {float_variable:.2f} 2) TBD
    +
    +Select examples:
    +
    +Format String: "telop-{date}" Blackboard: "date" is a blackboard variable with string value: "2021-05-13" Output: "teleop-2021-05-13"
    +
    +Format String: "{date}_loop_{loop_counter}" Blackboard: "date" is a blackboard variable with string value: "2021-05-13" Blackboard: "loop_counter" is a blackboard variable with integer value: "3" Output: "2021-05-13_loop_3"
    +
    +Format String: "battery charge is: {state.power_state.locomotion_charge_percentage.value}" Blackboard: "state" is a protobuf message in the blackboard from a BosdynRobotState, and the power_state submessage has a charge percentage of 30.2148320923085 Output: "battery charge is: 30.2158320923085"
    +
    +Format String: "battery charge is: {state.power_state.locomotion_charge_percentage.value:.2f}" Blackboard: "state" is a protobuf message in the blackboard from a BosdynRobotState, and the power_state submessage has a charge percentage of 30.2148320923085 Output: "battery charge is: 30.21"
    +
    +Format String: "the value is {x:.0f}" Blackboard: "x" is a blackboard variable with float value: "2.71828" Output: "the value is 3" |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Node
    +
    +Wrapper for a mission node. Contains the basics common to all mission nodes.
    +Specifics of what the node does are contained in the "impl" field.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| name | [string](#string) | Human-readable name of this node, e.g. "Goto waypoint 1", or "Power On". |
    +| user_data | [UserData](#bosdyn.api.mission.UserData) | Collection of user data associated with this node. |
    +| reference_id | [string](#string) | Reference identifier of this node. Set iff another node references this one. |
    +| impl | [google.protobuf.Any](#google.protobuf.Any) | Implementation of this node. For example, this may be a Sequence. |
    +| node_reference | [string](#string) | Unique identifier of another node. If this is filled out, rather than the "impl", then the referenced node will be used in place of this one. |
    +| parameter_values | [KeyValue](#bosdyn.api.mission.KeyValue) | Defines parameters, used by this node or its children. The "key" in KeyValue is the name of the parameter being defined. The value can be a constant or another parameter value. |
    +| overrides | [KeyValue](#bosdyn.api.mission.KeyValue) | Overwrites a protobuf field in this node's implementation. The "key" in KeyValue is the name of the field to override. The value to write can be sourced from a constant, or a parameter value. |
    +| parameters | [VariableDeclaration](#bosdyn.api.mission.VariableDeclaration) | Declares parameters needed at compile time by this node, or children of this node. This is a way for a node to communicate what parameters its implementation and/or children require, without unpacking the entire subtree. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Prompt
    +
    +Prompt the world at large to answer a question.
    +This node represents a request for information from ANY listeners that may be out there.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| always_reprompt | [bool](#bool) | Should we always re-prompt when this node is started? If false, this node will only ever prompt if it is started and its question is unanswered. This may be used, for example, to ask the user to check the robot after any self-right. If true, this node will prompt whenever it is started. This may be used, for example, to tell the user to perform some one-time action, like open a door for the robot. |
    +| text | [string](#string) | The text of the question itself. The question text may contain formatted blackboard variables. Please see the documentation in FormatBlackboard for more information about supported string formats. |
    +| source | [string](#string) | Metadata describing the source of the question. The answer will be written into the state blackboard with this as the variable name. |
    +| options | [Prompt.Option](#bosdyn.api.mission.Prompt.Option) | The set of options that can be chosen for this prompt. |
    +| child | [Node](#bosdyn.api.mission.Node) | Child node, run after the prompt has been responded to. Children will have access to the answer code provided by the response. |
    +| for_autonomous_processing | [bool](#bool) | Hint that Question posed by this Prompt is meant to be answered by some automated system. See the Question message for details. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Prompt.Option
    +
    +Data about the options to choose from.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| text | [string](#string) | Text associated with this option. Should be displayed to the user. |
    +| answer_code | [int64](#int64) | Numeric code corresponding to this option. Passed as part of the answer. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RemoteGrpc
    +
    +Call out to another system using the RemoteMission service.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| host | [string](#string) | Host that is running the directory server. Usually, this is just the robot. |
    +| service_name | [string](#string) | Name of the service in the directory. |
    +| timeout | [float](#float) | Timeout of any single RPC. If the timeout is exceeded, the RPC will fail. The mission service treats each failed RPC differently: - EstablishSession: An error is returned in LoadMission. - Tick: The RPC is retried. - Stop: The error is ignored, and the RPC is not retried. Omit for a default of 60 seconds. |
    +| lease_resources | [string](#string) | Resources that we will need leases on. |
    +| inputs | [KeyValue](#bosdyn.api.mission.KeyValue) | The list of variables the remote host should receive. Variables given can be available at either run-time or compile-time. The "key" in KeyValue is the name of the variable as used by the remote system. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Repeat
    +
    +Repeat a child node.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| max_starts | [int32](#int32) | Start the child node exactly this many times. Note that a value of 1 makes the Repeat node a no-op. |
    +| child | [Node](#bosdyn.api.mission.Node) | Child to repeat max_starts times. |
    +| start_counter_state_name | [string](#string) | If set, the node will write the start index to the blackboard. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RestartWhenPaused
    +
    +This node will run and return the status of the child node.
    +If the mission is paused while this node is executing, the child will be
    +restarted when the mission is resumed.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| child | [Node](#bosdyn.api.mission.Node) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RetainLease
    +
    +Send RetainLease for every Lease the mission service is given via PlayMissionRequest.
    +Returns RUNNING while there are more leases to retain, SUCCESS once a lease for each resource has
    +been retained, and FAILURE if any one lease cannot be retained.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| service_name | [string](#string) | Name of the service to use. |
    +| host | [string](#string) | Host machine of the directory server that the lease service is registered with. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Retry
    +
    +Retry a child node until it succeeds, or exceeds a number of attempts.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| max_attempts | [int32](#int32) | Only allow this many attempts. Note that a value of 1 makes this Retry node a no-op. |
    +| child | [Node](#bosdyn.api.mission.Node) | Child to retry up to max_attempts. |
    +| attempt_counter_state_name | [string](#string) | If set, the node will write the attempt index to the blackboard. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Selector
    +
    +Run all children in order until a child succeeds.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| always_restart | [bool](#bool) | Forces the execution to always begin with the first child. If false, and the Selector ran last tick, it will continue with the node it was ticking. |
    +| children | [Node](#bosdyn.api.mission.Node) | List of all children to iterate through. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Sequence
    +
    +Run  all children in order until a child fails.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| always_restart | [bool](#bool) | Forces the execution to always begin with the first child. If false, and the Sequence ran last tick, it will continue with the node it was ticking. |
    +| children | [Node](#bosdyn.api.mission.Node) | List of all children to iterate through. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetBlackboard
    +
    +Sets existing blackboard variables within this scope to specific values, returning SUCCESS.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| blackboard_variables | [KeyValue](#bosdyn.api.mission.KeyValue) | The key of the KeyValue is the name of the blackboard variable. The value will be dereferenced and converted into a value type at runtime inside this node's tick function. For example, if the value is a runtime variable, that variable will be evaluated at tick time, and then stored into the blackboard. If the value is another blackboard variable, that blackboard variable's value will be copied into the variable specified by the key. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SimpleParallel
    +
    +Run two child nodes together, returning the primary child's result when it completes.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| primary | [Node](#bosdyn.api.mission.Node) | Primary node, whose completion will end the execution of SimpleParallel. |
    +| secondary | [Node](#bosdyn.api.mission.Node) | Secondary node, which will be ticked as long as the primary is still running. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Sleep
    +
    +When started, begins a sleep timer for X seconds. Returns "success" after the timer elapses,
    +"running" otherwise.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| seconds | [float](#float) | Number of seconds to sleep for. |
    +| restart_after_stop | [bool](#bool) | If this node is stopped, should it restart the timer? |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SpotCamLed
    +
    +Set the LEDs to a specified brightness
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| service_name | [string](#string) | Name of the service to use. |
    +| host | [string](#string) | Host machine of the directory server that the Spot CAM registered with. |
    +| brightnesses | [SpotCamLed.BrightnessesEntry](#bosdyn.api.mission.SpotCamLed.BrightnessesEntry) | Brightnesses of the LEDs, from SetLEDBrightnessRequest |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SpotCamLed.BrightnessesEntry
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| key | [int32](#int32) |  |
    +| value | [float](#float) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SpotCamPtz
    +
    +Point the PTZ to a specified orientation
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| service_name | [string](#string) | Name of the service to use. |
    +| host | [string](#string) | Host machine of the directory server that the Spot CAM registered with. |
    +| ptz_position | [bosdyn.api.spot_cam.PtzPosition](#bosdyn.api.spot_cam.PtzPosition) | The rest of the fields are from bosdyn.api.spot_cam.ptz.SetPtzPositionRequest, see that message for details. |
    +| adjust_parameters | [SpotCamPtz.AdjustParameters](#bosdyn.api.mission.SpotCamPtz.AdjustParameters) | Setting adjust_parameters will enable auto-adjusting the PTZ pan and tilt at playback time, based on where the robot is, relative to the waypoint. Leave empty to disable auto-adjust features. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SpotCamPtz.AdjustParameters
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| localization_varname | [string](#string) | Variable name to retrieve the graph nav state from. |
    +| waypoint_id | [string](#string) | Waypoint ID where this PTZ configuration was originally set up. |
    +| waypoint_tform_body | [bosdyn.api.SE3Pose](#bosdyn.api.SE3Pose) | Pose of body in waypoint frame at the time this PTZ configuration was originally set up. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SpotCamResetAutofocus
    +
    +Reset the autofocus on the Spot CAM PTZ
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| service_name | [string](#string) | Name of the service to use. |
    +| host | [string](#string) | Host machine of the directory server that the Spot CAM registered with. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SpotCamStoreMedia
    +
    +Store media using the Spot CAM.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| service_name | [string](#string) | Name of the service to use. |
    +| host | [string](#string) | Host machine of the directory server that the Spot CAM registered with. |
    +| camera | [bosdyn.api.spot_cam.Camera](#bosdyn.api.spot_cam.Camera) | The rest of the fields are from bosdyn.api.spot_cam.logging.StoreRequest, see that message for details. |
    +| type | [bosdyn.api.spot_cam.Logpoint.RecordType](#bosdyn.api.spot_cam.Logpoint.RecordType) | What type of media should be stored from this action. |
    +| tag | [string](#string) | Extra metadata to store alongside the captured media. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StoreMetadata
    +
    +Triggers a StoreMetadataRequest to the data acquisition store.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| service_name | [string](#string) | Name of the service to use. |
    +| host | [string](#string) | Host machine of the directory server that the data acquisition service is registered with. |
    +| acquire_data_request_name | [string](#string) | The name of the blackboard variable that holds the associated AcquireDataRequest. The reference ID that this metadata is associated with will be copied from the request. |
    +| metadata_name | [string](#string) | The name of the metadata object in the blackboard to be stored. The metadata object can be any protobuf message. |
    +| metadata_channel | [string](#string) | The data buffer channel on which to store the metadata. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### Condition.Compare
    +
    +Comparison operator.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| COMPARE_UNKNOWN | 0 | Invalid, do not use. |
    +| COMPARE_EQ | 1 | Equal. |
    +| COMPARE_NE | 2 | Not equal. |
    +| COMPARE_LT | 3 | Less than. |
    +| COMPARE_GT | 4 | Greater than. |
    +| COMPARE_LE | 5 | Less than or equal. |
    +| COMPARE_GE | 6 | Greater than or equal. |
    +
    +
    +
    +
    +
    +### Condition.HandleStaleness
    +
    +When comparing runtime values in the blackboard, some values might be "stale" (i.e too old).
    +This defines how the comparator should behave when a read value is stale.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| HANDLE_STALE_UNKNOWN | 0 | acts like READ_ANYWAY for backwards compatibility. |
    +| HANDLE_STALE_READ_ANYWAY | 1 | ignore how stale this data is. |
    +| HANDLE_STALE_RUN_UNTIL_FRESH | 2 | return the RUNNING status until the data being read is not stale. |
    +| HANDLE_STALE_FAIL | 3 | return FAILURE status if stale data is read. |
    +
    +
    +
    +
    +
    +### DataAcquisition.CompletionBehavior
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| COMPLETE_UNKNOWN | 0 |  |
    +| COMPLETE_AFTER_SAVED | 1 | Node is complete after all data has been saved. |
    +| COMPLETE_AFTER_ACQUIRED | 2 | Node is complete after all data is acquired, but before processing and storage. This allows the robot to continue on with the mission sooner, but it will be unaware of failures in processing or storage. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# mission/remote.proto
    +
    +
    +
    +
    +
    +### EstablishSessionRequest
    +
    +Information to initialize a session to the remote service
    +for a particular mission node.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| leases | [bosdyn.api.Lease](#bosdyn.api.Lease) | All leases that the remote service may need. |
    +| inputs | [VariableDeclaration](#bosdyn.api.mission.VariableDeclaration) | Use this to provide other data (e.g. from the blackboard). The RemoteGrpc node will provide the name of the node automatically. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### EstablishSessionResponse
    +
    +Provide the id to use for the particular mission node to tick this remote service.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [EstablishSessionResponse.Status](#bosdyn.api.mission.EstablishSessionResponse.Status) | Result of this establish session request. |
    +| session_id | [string](#string) | On success, contains an ID for this session. |
    +| missing_lease_resources | [string](#string) | Need to provide leases on these resources. |
    +| lease_use_results | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | Details about how any leases were used. Allowed to be empty, if leases were not actually used. |
    +| missing_inputs | [VariableDeclaration](#bosdyn.api.mission.VariableDeclaration) | The inputs required by the contacted node that were not mentioned in the request. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StopRequest
    +
    +Used to stop a node that was previously ticked, so that it knows that
    +the next Tick represents a restart rather than a continuation.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| session_id | [string](#string) | Session ID as returned by the EstablishSessionResponse. Used to guarantee coherence between a single client and a servicer. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StopResponse
    +
    +Results of attempting to stop a remote node.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [StopResponse.Status](#bosdyn.api.mission.StopResponse.Status) | Result of the stop request. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### TeardownSessionRequest
    +
    +End the session originally established by an EstablishSessionRequest.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| session_id | [string](#string) | Session ID as returned by the EstablishSessionResponse. Used to guarantee coherence between a single client and a servicer. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### TeardownSessionResponse
    +
    +Results of ending a session.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [TeardownSessionResponse.Status](#bosdyn.api.mission.TeardownSessionResponse.Status) | The result of a TeardownSessionRequest. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### TickRequest
    +
    +Request that the remote tick itself for a particular node in the mission.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| session_id | [string](#string) | Session ID as returned by the EstablishSessionResponse. Used to guarantee coherence between a single client and a servicer. |
    +| leases | [bosdyn.api.Lease](#bosdyn.api.Lease) | All leases that the remote service may need. |
    +| inputs | [KeyValue](#bosdyn.api.mission.KeyValue) | Inputs provided to the servicer. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### TickResponse
    +
    +Response with the results of the tick.
    +Remote services should strive to return quickly, even if only returning RUNNING.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [TickResponse.Status](#bosdyn.api.mission.TickResponse.Status) | Result of the current tick. |
    +| missing_lease_resources | [string](#string) | Need to provide leases on these resources. |
    +| lease_use_results | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | Details about how any leases were used. Allowed to be empty, if leases were not actually used. |
    +| missing_inputs | [VariableDeclaration](#bosdyn.api.mission.VariableDeclaration) | Filled out when status is STATUS_MISSING_INPUTS, indicating what inputs were not in the request. |
    +| error_message | [string](#string) | If you need to report other error details, you can use this field. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### EstablishSessionResponse.Status
    +
    +Possible results of establishing a session.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Status unknown/unset. |
    +| STATUS_OK | 1 | Provided inputs / outputs are compatible. |
    +| STATUS_MISSING_LEASES | 2 | Remote service needs leases on additional resources. If set, the missing_lease_resources field should contain the resources needed but not provided. |
    +| STATUS_MISSING_INPUTS | 3 | Remote service needs additional inputs. |
    +
    +
    +
    +
    +
    +### StopResponse.Status
    +
    +Possible results for a StopRequest.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Status unknown/unset. |
    +| STATUS_OK | 1 | Service stopped. |
    +| STATUS_INVALID_SESSION_ID | 2 | The request provided an invalid session ID. |
    +
    +
    +
    +
    +
    +### TeardownSessionResponse.Status
    +
    +Possible results of ending a session.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Status unknown/unset. |
    +| STATUS_OK | 1 | Session was torn down -- servicer has probably wiped all associated data / state. |
    +| STATUS_INVALID_SESSION_ID | 2 | The request provided an invalid session ID. This may mean the session was already torn down. |
    +
    +
    +
    +
    +
    +### TickResponse.Status
    +
    +Possible results from the node. The FAILURE, RUNNING, and SUCCESS statuses map to the
    +behavior tree terms, all others indicate an error in the TickRequest.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Invalid; do not use. |
    +| STATUS_FAILURE | 1 | Node completed but failed. |
    +| STATUS_RUNNING | 2 | Node is processing and may finish in a future tick. |
    +| STATUS_SUCCESS | 3 | Node completed and succeeded. |
    +| STATUS_INVALID_SESSION_ID | 4 | The request provided an invalid session ID. |
    +| STATUS_MISSING_LEASES | 5 | The request was missing required leases. |
    +| STATUS_MISSING_INPUTS | 6 | The request was missing required inputs. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# mission/remote_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### RemoteMissionService
    +
    +Interface for mission callbacks.  Mission RemoteGrpc nodes will act as clients
    +to this service type, calling out to this service when loaded, ticked, or unloaded.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| EstablishSession | [EstablishSessionRequest](#bosdyn.api.mission.EstablishSessionRequest) | [EstablishSessionResponse](#bosdyn.api.mission.EstablishSessionResponse) | Call this once at mission load time, once for each node that references this remote service. |
    +| Tick | [TickRequest](#bosdyn.api.mission.TickRequest) | [TickResponse](#bosdyn.api.mission.TickResponse) | Call this every time the RemoteGrpc node is ticked. |
    +| Stop | [StopRequest](#bosdyn.api.mission.StopRequest) | [StopResponse](#bosdyn.api.mission.StopResponse) | Call this every time the RemoteGrpc node WAS ticked in the previous cycle, but was NOT ticked in this cycle. Signals that the next tick will be a restart, rather than a continuation. |
    +| TeardownSession | [TeardownSessionRequest](#bosdyn.api.mission.TeardownSessionRequest) | [TeardownSessionResponse](#bosdyn.api.mission.TeardownSessionResponse) | Tells the service it can forget any data associated with the given session ID. Should be called once for every EstablishSession call. |
    +
    + 
    +
    +
    +
    +
    +
    +# mission/util.proto
    +
    +
    +
    +
    +
    +### ConstantValue
    +
    +A constant value. Corresponds to the VariableDeclaration Type enum.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| float_value | [double](#double) |  |
    +| string_value | [string](#string) |  |
    +| int_value | [int64](#int64) |  |
    +| bool_value | [bool](#bool) |  |
    +| msg_value | [google.protobuf.Any](#google.protobuf.Any) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### KeyValue
    +
    +Key/Value pair, used in other messages.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| key | [string](#string) |  |
    +| value | [Value](#bosdyn.api.mission.Value) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### UserData
    +
    +Data a user can associate with a node.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| id | [string](#string) | Identifier. Enables matching the Node uploaded to the MissionService with the NodeInfo downloaded from the MissionService. |
    +| bytestring | [bytes](#bytes) | Arbitrary data. We recommend keeping it small, to avoid bloating the size of the mission. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Value
    +
    +A value of a run-time or compile-time variable.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| constant | [ConstantValue](#bosdyn.api.mission.ConstantValue) | A constant value. |
    +| runtime_var | [VariableDeclaration](#bosdyn.api.mission.VariableDeclaration) | Look up a variable provided at run-time. |
    +| parameter | [VariableDeclaration](#bosdyn.api.mission.VariableDeclaration) | Look up a Node Parameter. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### VariableDeclaration
    +
    +Declaration of a run-time or compile-time variable.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| name | [string](#string) | Name of the variable, to be used as the key in KeyValue pairs. |
    +| type | [VariableDeclaration.Type](#bosdyn.api.mission.VariableDeclaration.Type) | Type that this variable is expected to have. Used to verify assignments and comparisons. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### Result
    +
    +Results from executing / ticking / running a single node.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| RESULT_UNKNOWN | 0 | Invalid, should not be used. |
    +| RESULT_FAILURE | 1 | The node completed running, but failed. |
    +| RESULT_RUNNING | 2 | The node is still in process and has not completed. |
    +| RESULT_SUCCESS | 3 | The node completed, and succeeded. |
    +| RESULT_ERROR | 4 | The node encountered an operational error while trying to execute. |
    +
    +
    +
    +
    +
    +### VariableDeclaration.Type
    +
    +Supported types for blackboard or parameter values.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| TYPE_UNKNOWN | 0 |  |
    +| TYPE_FLOAT | 1 |  |
    +| TYPE_STRING | 2 |  |
    +| TYPE_INT | 3 |  |
    +| TYPE_BOOL | 4 |  |
    +| TYPE_MESSAGE | 5 |  |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# mobility_command.proto
    +
    +
    +
    +
    +
    +### MobilityCommand
    +
    +The robot command message to specify a basic command that moves the robot.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### MobilityCommand.Feedback
    +
    +The feedback for the mobility command that will provide information on the progress
    +of the robot command.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| se2_trajectory_feedback | [SE2TrajectoryCommand.Feedback](#bosdyn.api.SE2TrajectoryCommand.Feedback) | Feedback for the trajectory command. |
    +| se2_velocity_feedback | [SE2VelocityCommand.Feedback](#bosdyn.api.SE2VelocityCommand.Feedback) | Feedback for the velocity command. |
    +| sit_feedback | [SitCommand.Feedback](#bosdyn.api.SitCommand.Feedback) | Feedback for the sit command. |
    +| stand_feedback | [StandCommand.Feedback](#bosdyn.api.StandCommand.Feedback) | Feedback for the stand command. |
    +| stance_feedback | [StanceCommand.Feedback](#bosdyn.api.StanceCommand.Feedback) |  |
    +| stop_feedback | [StopCommand.Feedback](#bosdyn.api.StopCommand.Feedback) |  |
    +| follow_arm_feedback | [FollowArmCommand.Feedback](#bosdyn.api.FollowArmCommand.Feedback) |  |
    +| status | [RobotCommandFeedbackStatus.Status](#bosdyn.api.RobotCommandFeedbackStatus.Status) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### MobilityCommand.Request
    +
    +The mobility request must be one of the basic command primitives.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| se2_trajectory_request | [SE2TrajectoryCommand.Request](#bosdyn.api.SE2TrajectoryCommand.Request) | Command to move the robot along a trajectory. |
    +| se2_velocity_request | [SE2VelocityCommand.Request](#bosdyn.api.SE2VelocityCommand.Request) | Command to move the robot at a fixed velocity. |
    +| sit_request | [SitCommand.Request](#bosdyn.api.SitCommand.Request) | Command to sit the robot down. |
    +| stand_request | [StandCommand.Request](#bosdyn.api.StandCommand.Request) | Command to stand up the robot. |
    +| stance_request | [StanceCommand.Request](#bosdyn.api.StanceCommand.Request) |  |
    +| stop_request | [StopCommand.Request](#bosdyn.api.StopCommand.Request) |  |
    +| follow_arm_request | [FollowArmCommand.Request](#bosdyn.api.FollowArmCommand.Request) |  |
    +| params | [google.protobuf.Any](#google.protobuf.Any) | Robot specific command parameters. |
    +
    +
    +
    +
    +
    + 
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# network_compute_bridge.proto
    +
    +
    +
    +
    +
    +### ImageSourceAndService
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| image_source | [string](#string) | Image source. |
    +| image_service | [string](#string) | Image service. If blank, it is assumed to be the robot's default image service. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListAvailableModelsRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header |
    +| server_config | [NetworkComputeServerConfiguration](#bosdyn.api.NetworkComputeServerConfiguration) | Configuration about which server to use. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListAvailableModelsResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| available_models | [string](#string) | Provide list of available models |
    +| labels | [ModelLabels](#bosdyn.api.ModelLabels) | Optional information about available classes for each model |
    +| status | [ListAvailableModelsStatus](#bosdyn.api.ListAvailableModelsStatus) | Command status |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ModelLabels
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| model_name | [string](#string) | Model name. |
    +| available_labels | [string](#string) | List of class labels returned by this model. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### NetworkComputeInputData
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| image_source_and_service | [ImageSourceAndService](#bosdyn.api.ImageSourceAndService) | Image source to collect an image from. |
    +| image | [Image](#bosdyn.api.Image) | Image to process, if you are not using an image source. |
    +| other_data | [google.protobuf.Any](#google.protobuf.Any) | Other data that isn't an image. NetworkComputeBridge service will pass it through to the remote server so you can do computation on arbitrary data. |
    +| model_name | [string](#string) | Name of the model to be run on the input data. |
    +| min_confidence | [float](#float) | Minimum confidence [0.0 - 1.0] an object must have to be returned. Detections below this confidence threshold will be suppressed in the response. |
    +| rotate_image | [NetworkComputeInputData.RotateImage](#bosdyn.api.NetworkComputeInputData.RotateImage) | Options for rotating the image before processing. When unset, no rotation is applied. Rotation is supported for data from image services that provide a FrameTreeSnapshot defining the sensor's frame with respect to Spot's body and vision frames. Field is ignored for non-image input. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### NetworkComputeRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| input_data | [NetworkComputeInputData](#bosdyn.api.NetworkComputeInputData) | Input data. |
    +| server_config | [NetworkComputeServerConfiguration](#bosdyn.api.NetworkComputeServerConfiguration) | Configuration about which server to use. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### NetworkComputeResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| object_in_image | [WorldObject](#bosdyn.api.WorldObject) | Detection information. May include bounding boxes, image coordinates, 3D pose information, etc. |
    +| image_response | [ImageResponse](#bosdyn.api.ImageResponse) | The image we computed the data on. If the input image itself was provided in the request, this field is not populated. This field is not set for non-image input. |
    +| image_rotation_angle | [double](#double) | If the image was rotated for processing, this field will contain the amount it was rotated by (counter-clockwise, in radians).
    +
    +Note that the image returned is *not* rotated, regardless of if it was rotated for processing. This ensures that all other calibration and metadata remains valid. |
    +| other_data | [google.protobuf.Any](#google.protobuf.Any) | Non image-type data that can optionally be returned by a remote server. |
    +| status | [NetworkComputeStatus](#bosdyn.api.NetworkComputeStatus) | Command status |
    +
    +
    +
    +
    +
    +
    +
    +
    +### NetworkComputeServerConfiguration
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| service_name | [string](#string) | Service name in the robot's Directory for the worker that will process the request. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### ListAvailableModelsStatus
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| LIST_AVAILABLE_MODELS_STATUS_UNKNOWN | 0 | Status is not specified. |
    +| LIST_AVAILABLE_MODELS_STATUS_SUCCESS | 1 | Succeeded. |
    +| LIST_AVAILABLE_MODELS_STATUS_EXTERNAL_SERVICE_NOT_FOUND | 2 | External service not found in the robot's directory. |
    +| LIST_AVAILABLE_MODELS_STATUS_EXTERNAL_SERVER_ERROR | 3 | The call to the external server did not succeed. |
    +
    +
    +
    +
    +
    +### NetworkComputeInputData.RotateImage
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| ROTATE_IMAGE_UNKNOWN | 0 | Unspecified rotation method. Do not use. |
    +| ROTATE_IMAGE_NO_ROTATION | 3 | No rotation applied. |
    +| ROTATE_IMAGE_ALIGN_HORIZONTAL | 1 | Rotate the images so the horizon is not rolled with respect to gravity. |
    +| ROTATE_IMAGE_ALIGN_WITH_BODY | 2 | Rotate the images so that the horizon in the image is aligned with the inclination of the body. For example, when applied to the left body camera this option rotates the image so that the world does not appear upside down when the robot is standing upright, but if the body is pitched up, the image will appear rotated. |
    +
    +
    +
    +
    +
    +### NetworkComputeStatus
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| NETWORK_COMPUTE_STATUS_UNKNOWN | 0 | Status is not specified. |
    +| NETWORK_COMPUTE_STATUS_SUCCESS | 1 | Succeeded. |
    +| NETWORK_COMPUTE_STATUS_EXTERNAL_SERVICE_NOT_FOUND | 2 | External service not found in the robot's directory. |
    +| NETWORK_COMPUTE_STATUS_EXTERNAL_SERVER_ERROR | 3 | The call to the external server did not succeed. |
    +| NETWORK_COMPUTE_STATUS_ROTATION_ERROR | 4 | The robot failed to rotate the image as requested. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# network_compute_bridge_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### NetworkComputeBridge
    +
    +RPCs for sending images or other data to networked server for computation.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| NetworkCompute | [NetworkComputeRequest](#bosdyn.api.NetworkComputeRequest) | [NetworkComputeResponse](#bosdyn.api.NetworkComputeResponse) |  |
    +| ListAvailableModels | [ListAvailableModelsRequest](#bosdyn.api.ListAvailableModelsRequest) | [ListAvailableModelsResponse](#bosdyn.api.ListAvailableModelsResponse) |  |
    +
    +
    +
    +
    +### NetworkComputeBridgeWorker
    +
    +Set of RPCs for workers of the network compute bridge.  This is seperate from the RPCs for the
    +on-robot network compute bridge so that if they need to diverge in the future it is possible
    +to do so.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| NetworkCompute | [NetworkComputeRequest](#bosdyn.api.NetworkComputeRequest) | [NetworkComputeResponse](#bosdyn.api.NetworkComputeResponse) |  |
    +| ListAvailableModels | [ListAvailableModelsRequest](#bosdyn.api.ListAvailableModelsRequest) | [ListAvailableModelsResponse](#bosdyn.api.ListAvailableModelsResponse) |  |
    +
    + 
    +
    +
    +
    +
    +
    +# network_stats.proto
    +
    +
    +
    +
    +
    +### Association
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| mac_address | [string](#string) | MAC address of the associated station |
    +| connected_time | [google.protobuf.Duration](#google.protobuf.Duration) | Time duration since the station last connected. |
    +| rx_signal_dbm | [int32](#int32) | Signal strength of last received packet |
    +| rx_signal_avg_dbm | [int32](#int32) | Signal strength average |
    +| rx_beacon_signal_avg_dbm | [int32](#int32) | Signal strength average for beacons only. |
    +| expected_bits_per_second | [int64](#int64) | Expected throughput |
    +| rx_bytes | [int64](#int64) | Total received bytes |
    +| rx_packets | [int64](#int64) | Total received packets from the associated station |
    +| rx_bits_per_second | [int64](#int64) | Last unicast receive rate |
    +| tx_bytes | [int64](#int64) | Total transmitted bytes |
    +| tx_packets | [int64](#int64) | Total transmitted packets to the associated station |
    +| tx_bits_per_second | [int64](#int64) | Current unicast transmit rate |
    +| tx_retries | [int64](#int64) | Cumulative retry count to this station, within connected time |
    +| tx_failed | [int64](#int64) | Cumulative failed tx packet count to this station, within connected time |
    +| beacons_received | [int64](#int64) | Number of beacons received from this peer |
    +| beacon_loss_count | [int64](#int64) | Number of times beacon loss was detected |
    +
    +
    +
    +
    +
    +
    +
    +
    +### WifiDevice
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| type | [WifiDevice.Type](#bosdyn.api.WifiDevice.Type) |  |
    +| name | [string](#string) |  |
    +| mac_address | [string](#string) |  |
    +| ssid | [string](#string) |  |
    +| tx_power_dbm | [int32](#int32) |  |
    +| associations | [Association](#bosdyn.api.Association) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### WifiStats
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| hostname | [string](#string) |  |
    +| devices | [WifiDevice](#bosdyn.api.WifiDevice) |  |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### WifiDevice.Type
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| UNKNOWN | 0 |  |
    +| AP | 1 |  |
    +| CLIENT | 2 |  |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# parameter.proto
    +
    +
    +
    +
    +
    +### Parameter
    +
    +A generic parameter message used by the robot state service to describe different,
    +parameterized aspects of the robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| label | [string](#string) | Name of parameter. |
    +| units | [string](#string) | Units of parameter value. |
    +| int_value | [int64](#int64) | Value of a countable measure. |
    +| float_value | [double](#double) | Value of a continuous measure. |
    +| timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | A point in time. |
    +| duration | [google.protobuf.Duration](#google.protobuf.Duration) | A time duration. |
    +| string_value | [string](#string) | Value as a string. |
    +| bool_value | [bool](#bool) | Value as true/false. |
    +| uint_value | [uint64](#uint64) | Unsigned integer |
    +| notes | [string](#string) | Description of the parameter or its value. |
    +
    +
    +
    +
    +
    + 
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# payload.proto
    +
    +
    +
    +
    +
    +### JointLimits
    +
    +JointLimits contain hip joint angles where limb to payload collisions occur.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| label | [string](#string) | Label identifying the respective limb to which these apply [fr,fl,hr,hl] |
    +| hy | [float](#float) | (hy, hx) coordinates outlining the hip joint limits where collisions occur between robot hip and payload. Paired vectors must be of equal length. Angles are measured with actual contact. Appropriate margin will be provided in software. Radians. Left legs must have hx > 0. Right legs must have hx < 0. |
    +| hx | [float](#float) | All legs must have hy > 1.3. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListPayloadsRequest
    +
    +The ListPayloads request message sent to the robot to get all known payloads.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListPayloadsResponse
    +
    +The ListPayloads response message returns all payloads registered in the robot's directory.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| payloads | [Payload](#bosdyn.api.Payload) | The returned list of payloads registered in the directory. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### MomentOfIntertia
    +
    +Structure describing the moment of intertia of a body. The xx, yy, zz fields
    +are the diagonal of the MOI tensor, and the xy, xz, and yz fields are the
    +off diagonal terms.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| xx | [float](#float) |  |
    +| yy | [float](#float) |  |
    +| zz | [float](#float) |  |
    +| xy | [float](#float) |  |
    +| xz | [float](#float) |  |
    +| yz | [float](#float) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Payload
    +
    +A Payload describes a single payload installed on the Spot platform.
    +It includes all external information necessary to represent
    +the payload to the user as a single record.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| GUID | [string](#string) | A unique id provided by the payload or auto-generated by the website. |
    +| name | [string](#string) | A human readable name describing this payload. It is provided by the payload as part of the payload announcement system. |
    +| description | [string](#string) | A human-readable description string providing more context as to the function of this payload. It is displayed in UIs. |
    +| label_prefix | [string](#string) | A list of labels used to indicate what type of payload this is. |
    +| is_authorized | [bool](#bool) | Set true once the payload is authorized by the administrator in the payload webpage. Must be set to false at registration time. |
    +| is_enabled | [bool](#bool) | Set true if the payload is attached to the robot. Must be set to false at registration time. |
    +| is_noncompute_payload | [bool](#bool) | Set true for payloads registered without their own computers. These records are all manually entered. |
    +| version | [SoftwareVersion](#bosdyn.api.SoftwareVersion) | Payload version details. |
    +| body_tform_payload | [SE3Pose](#bosdyn.api.SE3Pose) | The pose of the payload relative to the body frame. |
    +| mount_tform_payload | [SE3Pose](#bosdyn.api.SE3Pose) | The pose of the payload relative to the mount frame. |
    +| mount_frame_name | [MountFrameName](#bosdyn.api.MountFrameName) | Optional - mount frame_name (if not included, payload is assumed to be in the body mount frame) |
    +| mass_volume_properties | [PayloadMassVolumeProperties](#bosdyn.api.PayloadMassVolumeProperties) | The mass and volume properties of the payload. |
    +| preset_configurations | [PayloadPreset](#bosdyn.api.PayloadPreset) | A list of possible physical configurations for the payload. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PayloadMassVolumeProperties
    +
    +PayloadMassVolumeProperties contain mass and volume information for the payload
    +in the format that the user interacts with it. It is transmitted to the control
    +and perception systems and processed there to inform those systems.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| total_mass | [float](#float) | Total mass of payload in kg. |
    +| com_pos_rt_payload | [Vec3](#bosdyn.api.Vec3) | Position of the center of mass of the payload in the payload frame. Meters. |
    +| moi_tensor | [MomentOfIntertia](#bosdyn.api.MomentOfIntertia) | The moment of inertia of the payload, represented about the payload center of mass, in the payload frame. Units in [kg*m^2]. |
    +| bounding_box | [Box3WithFrame](#bosdyn.api.Box3WithFrame) | Zero or more bounding boxes indicating the occupied volume of the payload. These boxes must be represented in the payload frame by specifying Must have Box3WithFrame.frame_name == "payload". |
    +| joint_limits | [JointLimits](#bosdyn.api.JointLimits) | Joint limits defining limits to range of motion of the hips of the robot, in order to prevent collisions with the payload. This field is optional and is only recommended for advanced development purposes. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PayloadPreset
    +
    +The physical configurations for the payload.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| preset_name | [string](#string) | A human readable name describing this configuration. It is displayed in the admin console, but will not overwrite the top level payload name. |
    +| description | [string](#string) | A human-readable description providing context on this configuration. It is displayed in the admin console. |
    +| mount_tform_payload | [SE3Pose](#bosdyn.api.SE3Pose) | The pose of the payload relative to the body frame. |
    +| mount_frame_name | [MountFrameName](#bosdyn.api.MountFrameName) | Optional - mount frame_name (if not included, payload is assumed to be in the body mount frame) |
    +| mass_volume_properties | [PayloadMassVolumeProperties](#bosdyn.api.PayloadMassVolumeProperties) | The mass and volume properties of the payload. |
    +| label_prefix | [string](#string) | A list of labels used to indicate what type of payload this is. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### MountFrameName
    +
    +Payloads are defined relative to a frame on the robot. These are the possible frames.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| MOUNT_FRAME_UNKNOWN | 0 | The is the default. For backwards compatibility, we assume unknown means body mount frame. |
    +| MOUNT_FRAME_BODY_PAYLOAD | 1 | The body payload mount frame, as defined in documentation. |
    +| MOUNT_FRAME_GRIPPER_PAYLOAD | 2 | The gripper payload mount frame, as defined in documentation. |
    +| MOUNT_FRAME_WR1 | 3 | The wrist link frame, as defined in the gripper CAD and documentation. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# payload_estimation.proto
    +
    +
    +
    +
    +
    +### PayloadEstimationCommand
    +
    +Command the robot to stand and execute a routine to estimate the mass properties of an
    +unregistered payload attached to the robot.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### PayloadEstimationCommand.Feedback
    +
    +The PayloadEstimationCommand provides several pieces of feedback:
    +  - If the routine is finished running (and its current progress).
    +  - If the routine encountered any errors while running.
    +  - The resulting payload estimated by the routine.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| status | [PayloadEstimationCommand.Feedback.Status](#bosdyn.api.PayloadEstimationCommand.Feedback.Status) | Status of the estimation routine. |
    +| progress | [float](#float) | The approximate progress of the routine, range [0-1]. |
    +| error | [PayloadEstimationCommand.Feedback.Error](#bosdyn.api.PayloadEstimationCommand.Feedback.Error) | Error status of the estimation routine. |
    +| estimated_payload | [Payload](#bosdyn.api.Payload) | The resulting payload estimated by the estimation routine. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PayloadEstimationCommand.Request
    +
    +PayloadEstimation command request takes no additional arguments. The estimation routine
    +takes about ~1min to run. Subsequent PayloadEstimationCommand requests issued while the 
    +routine is in progress are ignored until the routine is completed.
    +
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### PayloadEstimationCommand.Feedback.Error
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| ERROR_UNKNOWN | 0 |  |
    +| ERROR_NONE | 1 | No error has occurred. |
    +| ERROR_FAILED_STAND | 2 | Robot failed to stand/change stance. |
    +| ERROR_NO_RESULTS | 3 | Failed to calculate results. |
    +
    +
    +
    +
    +
    +### PayloadEstimationCommand.Feedback.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 |  |
    +| STATUS_COMPLETED | 1 | Completed estimation routine successfully; estimated_payload is populated. |
    +| STATUS_SMALL_MASS | 2 | Completed estimation routine successfully, but estimated mass is small enough to not significantly impact mobility; estimated_payload is empty. |
    +| STATUS_IN_PROGRESS | 3 | Estimation routine is currently running; estimated_payload is empty. |
    +| STATUS_ERROR | 4 | Error occurred during the routine; estaimted_payload is empty. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# payload_registration.proto
    +
    +
    +
    +
    +
    +### GetPayloadAuthTokenRequest
    +
    +Request a user token from the robot
    +A token will only be provided after the registered payload has been enabled by an admin.
    +The returned user token will have limited access to the services necessary for a simple payload.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| payload_credentials | [PayloadCredentials](#bosdyn.api.PayloadCredentials) | Payload credentials. |
    +| payload_guid | [string](#string) | The GUID to identify which payload to get the auth token for. |
    +| payload_secret | [string](#string) | The payload secret for the specified payload. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetPayloadAuthTokenResponse
    +
    +The GetPayloadAuthToken response message that returns the token for the payload.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [GetPayloadAuthTokenResponse.Status](#bosdyn.api.GetPayloadAuthTokenResponse.Status) | Return status for the request. |
    +| token | [string](#string) | A limited-access user token provided on successful payload registration |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PayloadCredentials
    +
    +PayloadCredentials are used to authorize a payload.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| guid | [string](#string) | The GUID of the payload. |
    +| secret | [string](#string) | The secret of the payload. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RegisterPayloadRequest
    +
    +The RegisterPayload request message contains the payload information and secret to be
    +able to register it to the directory.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| payload | [Payload](#bosdyn.api.Payload) | The payload to register, which must have, at minimum, GUID specified correctly. The admin console can be used to verify that the payload definition is valid after registration. |
    +| payload_secret | [string](#string) | A private string provided by the payload to verify identity for auth. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RegisterPayloadResponse
    +
    +The RegisterPayload response message contains the status of whether the payload was successfully
    +registered to the directory.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [RegisterPayloadResponse.Status](#bosdyn.api.RegisterPayloadResponse.Status) | Return status for the request. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### UpdatePayloadAttachedRequest
    +
    +Attach/detach the payload with the matching GUID. The existing payload must
    +have a secret set and the request must provide the secret for access.
    +GUID is immutable and cannot be updated.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| payload_credentials | [PayloadCredentials](#bosdyn.api.PayloadCredentials) | Payload credentials, used to identify the payload and authorize the changes. |
    +| request | [UpdatePayloadAttachedRequest.Request](#bosdyn.api.UpdatePayloadAttachedRequest.Request) | Attach or detach the payload. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### UpdatePayloadAttachedResponse
    +
    +The UpdatePayloadAttached response message contains the status of whether the update was successful.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [UpdatePayloadAttachedResponse.Status](#bosdyn.api.UpdatePayloadAttachedResponse.Status) | Return status for the request. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### UpdatePayloadVersionRequest
    +
    +Update the payload definition of the payload with matching GUID. The existing payload must
    +have a secret set and the request must provide the secret for access.
    +GUID and is_authorized fields are immutable and cannot be updated.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| payload_credentials | [PayloadCredentials](#bosdyn.api.PayloadCredentials) | Payload credentials. |
    +| payload_guid | [string](#string) | The GUID of the payload to be updated. |
    +| payload_secret | [string](#string) | The payload secret for the specified payload. |
    +| updated_version | [SoftwareVersion](#bosdyn.api.SoftwareVersion) | The new software version that the payload is being updated to. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### UpdatePayloadVersionResponse
    +
    +The UpdatePayloadVersion response message contains the status of whether the update was successful.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [UpdatePayloadVersionResponse.Status](#bosdyn.api.UpdatePayloadVersionResponse.Status) | Return status for the request. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### GetPayloadAuthTokenResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | UNKNOWN should never be used. An internal PayloadRegistrationService issue has happened if UNKNOWN is set. |
    +| STATUS_OK | 1 | Success. The token is available. |
    +| STATUS_INVALID_CREDENTIALS | 2 | GetPayloadAuthToken failed because the paylod guid & secret do not match any registered payloads. |
    +| STATUS_PAYLOAD_NOT_AUTHORIZED | 3 | GetPayloadAuthToken failed because the paylod has not been authorized by an admin. |
    +
    +
    +
    +
    +
    +### RegisterPayloadResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | UNKNOWN should never be used. An internal PayloadRegistrationService issue has happened if UNKNOWN is set. |
    +| STATUS_OK | 1 | Success. The new service record is available. |
    +| STATUS_ALREADY_EXISTS | 2 | RegisterPayload failed because a payload with this GUID already exists. |
    +
    +
    +
    +
    +
    +### UpdatePayloadAttachedRequest.Request
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| REQUEST_UNKNOWN | 0 |  |
    +| REQUEST_ATTACH | 1 |  |
    +| REQUEST_DETACH | 2 |  |
    +
    +
    +
    +
    +
    +### UpdatePayloadAttachedResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | UNKNOWN should never be used. An internal PayloadRegistrationService issue has happened if UNKNOWN is set. |
    +| STATUS_OK | 1 | Success. The payload version has been updated. |
    +| STATUS_DOES_NOT_EXIST | 2 | UpdatePayloadAttached failed because a payload with this GUID does not yet exist. |
    +| STATUS_INVALID_CREDENTIALS | 3 | UpdatePayloadAttached failed because the paylod guid & secret do not match any registered payloads. |
    +| STATUS_PAYLOAD_NOT_AUTHORIZED | 4 | UpdatePayloadAttached failed because the requested payload has not yet been authorized. Authorize the payload in the webserver first, then try again. |
    +
    +
    +
    +
    +
    +### UpdatePayloadVersionResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | UNKNOWN should never be used. An internal PayloadRegistrationService issue has happened if UNKNOWN is set. |
    +| STATUS_OK | 1 | Success. The payload version has been updated. |
    +| STATUS_DOES_NOT_EXIST | 2 | UpdatePayload failed because a payload with this GUID does not yet exist. |
    +| STATUS_INVALID_CREDENTIALS | 3 | UpdatePayload failed because the paylod guid & secret do not match any registered payloads. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# payload_registration_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### PayloadRegistrationService
    +
    +This service provides a way to register new payloads.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| RegisterPayload | [RegisterPayloadRequest](#bosdyn.api.RegisterPayloadRequest) | [RegisterPayloadResponse](#bosdyn.api.RegisterPayloadResponse) | Register a payload with the directory. |
    +| UpdatePayloadVersion | [UpdatePayloadVersionRequest](#bosdyn.api.UpdatePayloadVersionRequest) | [UpdatePayloadVersionResponse](#bosdyn.api.UpdatePayloadVersionResponse) | Update the version for the registered payload. |
    +| GetPayloadAuthToken | [GetPayloadAuthTokenRequest](#bosdyn.api.GetPayloadAuthTokenRequest) | [GetPayloadAuthTokenResponse](#bosdyn.api.GetPayloadAuthTokenResponse) | Get the authentication token information associated with a given payload. |
    +| UpdatePayloadAttached | [UpdatePayloadAttachedRequest](#bosdyn.api.UpdatePayloadAttachedRequest) | [UpdatePayloadAttachedResponse](#bosdyn.api.UpdatePayloadAttachedResponse) | Tell the robot whether the specified payload is attached.. |
    +
    + 
    +
    +
    +
    +
    +
    +# payload_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### PayloadService
    +
    +This service provides a way to query for the currently-registered payloads.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| ListPayloads | [ListPayloadsRequest](#bosdyn.api.ListPayloadsRequest) | [ListPayloadsResponse](#bosdyn.api.ListPayloadsResponse) | List all payloads the robot knows about. |
    +
    + 
    +
    +
    +
    +
    +
    +# point_cloud.proto
    +
    +
    +
    +
    +
    +### GetPointCloudRequest
    +
    +The GetPointCloud request message to ask a specific point cloud service for data.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| point_cloud_requests | [PointCloudRequest](#bosdyn.api.PointCloudRequest) | Sources to retrieve from. The service will return a response for each PointCloudRequest. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetPointCloudResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| point_cloud_responses | [PointCloudResponse](#bosdyn.api.PointCloudResponse) | The resulting point clouds for each requested source. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListPointCloudSourcesRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListPointCloudSourcesResponse
    +
    +The GetPointCloud response message which returns any point cloud data associated with that service.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response Header. |
    +| point_cloud_sources | [PointCloudSource](#bosdyn.api.PointCloudSource) | The set of PointCloudSources available from this service. May be empty if the service serves no point clouds (e.g., if no sensors were found on startup). |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PointCloud
    +
    +Data from a point-cloud producing sensor or process.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| source | [PointCloudSource](#bosdyn.api.PointCloudSource) | The sensor or process that produced the point cloud. |
    +| num_points | [int32](#int32) | The number of points in the point cloud. |
    +| encoding | [PointCloud.Encoding](#bosdyn.api.PointCloud.Encoding) | Representation of the underlying point cloud data. |
    +| encoding_parameters | [PointCloud.EncodingParameters](#bosdyn.api.PointCloud.EncodingParameters) | Constants needed to decode the point cloud. |
    +| data | [bytes](#bytes) | Raw byte data representing the points. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PointCloud.EncodingParameters
    +
    +Parameters needed to decode the point cloud.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| scale_factor | [int32](#int32) | Used in the remapping process from bytes to metric units. (unitless) |
    +| max_x | [double](#double) | In XYZ_4SC and XYZ_5SC, the point cloud is assumed to lie inside a box centered in the data frame. max_x, max_y, max_z are half the dimensions of that box. These dimensions should be assumed to be meters. |
    +| max_y | [double](#double) | max_y is half the dimensions of the assumed box (for XYZ_4SC and XYZ_5SC). These dimensions should be assumed to be meters. |
    +| max_z | [double](#double) | max_z is half the dimensions of the assumed box (for XYZ_4SC and XYZ_5SC). These dimensions should be assumed to be meters. |
    +| remapping_constant | [double](#double) | Used in the remapping process from bytes to metric units. (unitless) For XYZ_4SC and XYZ_5C, this should equal 127. |
    +| bytes_per_point | [int32](#int32) | Number of bytes in each point in this encoding. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PointCloudRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| point_cloud_source_name | [string](#string) | Name of the point cloud source to request from. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PointCloudResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| status | [PointCloudResponse.Status](#bosdyn.api.PointCloudResponse.Status) | Return status for the request. |
    +| point_cloud | [PointCloud](#bosdyn.api.PointCloud) | The current point cloud from the service. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PointCloudSource
    +
    +Information about a sensor or process that produces point clouds.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| name | [string](#string) | The name of the point cloud source. This is intended to be unique accross all point cloud sources, and should be human readable. |
    +| frame_name_sensor | [string](#string) | The frame name of the sensor. The transformation from vision_tform_sensor can be computed by traversing the tree in the FrameTreeSnapshot. |
    +| acquisition_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Time that the data was produced on the sensor in the robot's clock. |
    +| transforms_snapshot | [FrameTreeSnapshot](#bosdyn.api.FrameTreeSnapshot) | A tree-based collection of transformations, which will include the transformations to the point cloud data frame and the point cloud sensor frame. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### PointCloud.Encoding
    +
    +Point clouds may be encoded in different ways to preserve bandwidth or disk space.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| ENCODING_UNKNOWN | 0 | The point cloud has an unknown encoding. |
    +| ENCODING_XYZ_32F | 1 | Each point is x,y,z float32 value (12 bytes, little-endian) stored sequentially. This allows the point cloud to be expressed in any range and resolution represented by floating point numbers, but the point cloud will be larger than if one of the other encodings is used. |
    +| ENCODING_XYZ_4SC | 2 | Each point is 3 signed int8s plus an extra shared signed int8s (4 byte). byte layout: [..., p1_x, p1_y, p1_z, x, ...] Each coordinate is mapped to a value between -1 and +1 (corresponding to a minimum and maximum range). The resulting point is: P = remap(p1 * f + p2, c * f, m) Where: p1 = the highest byte in each dimension of the point. p2 = a vector of "extra" bytes converted to metric units. = [mod (x, f), mod(x/f, f), mod(x/(f^2), f)] - f/2 x = the "extra" byte for each point. f = An integer scale factor. m = [max_x, max_y, max_z], the point cloud max bounds in meters. c = a remapping constant. And: remap(a, b, c) = (a + b)/(2 * b) - c Point clouds use 1/3 the memory of XYZ_32F, but have limits on resolution and range. Points must not lie outside of the box of size [-m, m]. Within that box, the resolution of the point cloud will depend on the encoding parameters. For example if m = [10, 10, 10], and f = 5 with c = 127 the resolution is approximately 1.5 cm per point. |
    +| ENCODING_XYZ_5SC | 3 | Each point is 3 signed int8s plus two extra shared signed int8s (5 byte). The encoding is the same as XYZ_4SC, except the "extra" value x is a 16 bit integer. This encoding has roughly double the resolution of XYZ_4SC, but takes up an additional byte for each point. |
    +
    +
    +
    +
    +
    +### PointCloudResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | UNKNOWN should never be used. An internal PointCloudService issue has happened if UNKNOWN is set. None of the other fields are filled out. |
    +| STATUS_OK | 1 | Call succeeded at filling out all the fields. |
    +| STATUS_SOURCE_DATA_ERROR | 2 | Failed to fill out PointCloudSource. All the other fields are not filled out. |
    +| STATUS_POINT_CLOUD_DATA_ERROR | 3 | There was a problem with the point cloud data. Only the PointCloudSource is filled out. |
    +| STATUS_UNKNOWN_SOURCE | 4 | Provided point cloud source was not found. One |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# point_cloud_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### PointCloudService
    +
    +The point cloud service provides access to one or more point cloud sources, for example
    +from a lidar. It supports querying the list of available sources provided by the service
    +and it supports requesting the latest point cloud data for each source by name.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| ListPointCloudSources | [ListPointCloudSourcesRequest](#bosdyn.api.ListPointCloudSourcesRequest) | [ListPointCloudSourcesResponse](#bosdyn.api.ListPointCloudSourcesResponse) | Obtain the list of PointCloudSources for this given service. Note that there may be multiple PointCloudServices running, each with their own set of sources The name field keys access to individual point clouds when calling GetPointCloud. |
    +| GetPointCloud | [GetPointCloudRequest](#bosdyn.api.GetPointCloudRequest) | [GetPointCloudResponse](#bosdyn.api.GetPointCloudResponse) | Request point clouds by source name. |
    +
    + 
    +
    +
    +
    +
    +
    +# power.proto
    +
    +
    +
    +
    +
    +### PowerCommandFeedbackRequest
    +
    +The PowerCommandFeedback request message, which can get the feedback for a specific
    +power command id number.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| power_command_id | [uint32](#uint32) | Unique identifier for the command of which feedback is desired. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PowerCommandFeedbackResponse
    +
    +The PowerCommandFeedback response message, which contains the progress of the power command.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [PowerCommandStatus](#bosdyn.api.PowerCommandStatus) | Current status of specified command. |
    +| blocking_faults | [SystemFault](#bosdyn.api.SystemFault) | Optional list of active faults blocking success of the PowerCommandRequest |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PowerCommandRequest
    +
    +The PowerCommand request which specifies a change in the robot's motor power.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| lease | [Lease](#bosdyn.api.Lease) | The Lease to show ownership of the robot. |
    +| request | [PowerCommandRequest.Request](#bosdyn.api.PowerCommandRequest.Request) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PowerCommandResponse
    +
    +The PowerCommand response message which contains a unique identifier that can be used to
    +get feedback on the progress of a power command from the power service.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| lease_use_result | [LeaseUseResult](#bosdyn.api.LeaseUseResult) | Details about how the lease was used. |
    +| status | [PowerCommandStatus](#bosdyn.api.PowerCommandStatus) | Current feedback of specified command. |
    +| power_command_id | [uint32](#uint32) | Unique identifier for the command, If empty, was not accepted. |
    +| license_status | [LicenseInfo.Status](#bosdyn.api.LicenseInfo.Status) | License check status |
    +| blocking_faults | [SystemFault](#bosdyn.api.SystemFault) | Optional list of active faults blocking success of the PowerCommandRequest |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### PowerCommandRequest.Request
    +
    +Commands for the robot to execute.
    +Note that not all Spot robots are compatible with all these commands. Check your robot's
    +HardwareConfiguration in bosdyn.api.robot_state.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| REQUEST_UNKNOWN | 0 | Invalid request; do not use. |
    +| REQUEST_OFF | 1 | Cut power to motors immediately. |
    +| REQUEST_ON | 2 | Turn on power to the robot motors. |
    +| REQUEST_OFF_MOTORS | 1 | Cut power to motors immediately. |
    +| REQUEST_ON_MOTORS | 2 | Turn on power to the robot motors. |
    +| REQUEST_OFF_ROBOT | 3 | Turn off the robot. Same as physical switch. |
    +| REQUEST_CYCLE_ROBOT | 4 | Power cycle the robot. Same as physical switch. |
    +| REQUEST_OFF_PAYLOAD_PORTS | 5 | Cut power to the payload ports. |
    +| REQUEST_ON_PAYLOAD_PORTS | 6 | Turn on power to the payload ports. |
    +| REQUEST_OFF_WIFI_RADIO | 7 | Cut power to the hardware Wi-Fi radio. |
    +| REQUEST_ON_WIFI_RADIO | 8 | Power on the hardware Wi-Fi radio. |
    +
    +
    +
    +
    +
    +### PowerCommandStatus
    +
    +Feedback on the current state of a power command on the robot.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Status is not specified. |
    +| STATUS_IN_PROGRESS | 1 | Power command is executing. |
    +| STATUS_SUCCESS | 2 | Power command succeeded. |
    +| STATUS_SHORE_POWER_CONNECTED | 3 | ERROR: Robot cannot be powered on while on wall power. |
    +| STATUS_BATTERY_MISSING | 4 | ERROR: Battery not inserted into robot. |
    +| STATUS_COMMAND_IN_PROGRESS | 5 | ERROR: Power command cant be overwritten. |
    +| STATUS_ESTOPPED | 6 | ERROR: Cannot power on while estopped. A robot may have multiple estops. Inspect EStopState for additional info. |
    +| STATUS_FAULTED | 7 | ERROR: Cannot power due to a fault. Inspect FaultState for more info. |
    +| STATUS_INTERNAL_ERROR | 8 | ERROR: Internal error occurred, may be clear-able by issuing a power off command. |
    +| STATUS_LICENSE_ERROR | 9 | ERROR: License check failed. Check license_status field for details. |
    +| INCOMPATIBLE_HARDWARE_ERROR | 10 | ERROR: The Spot hardware is not compatible with the request request. |
    +| STATUS_OVERRIDDEN | 11 | ERROR: Robot has overridden the power command and disabled motor power. In the case of a commanded power OFF, robot will report SUCCESS if power is disabled. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# power_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### PowerService
    +
    +The power service for the robot that can power on/off the robot's motors.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| PowerCommand | [PowerCommandRequest](#bosdyn.api.PowerCommandRequest) | [PowerCommandResponse](#bosdyn.api.PowerCommandResponse) | Starts a power command on the robot. A robot can only accept one power command at once. Power commands, are not interruptible. Once a command is issued, it must complete before another command can be issued. |
    +| PowerCommandFeedback | [PowerCommandFeedbackRequest](#bosdyn.api.PowerCommandFeedbackRequest) | [PowerCommandFeedbackResponse](#bosdyn.api.PowerCommandFeedbackResponse) | Check the status of a power command. |
    +
    + 
    +
    +
    +
    +
    +
    +# robot_command.proto
    +
    +
    +
    +
    +
    +### ClearBehaviorFaultRequest
    +
    +A ClearBehaviorFault request message has the associated behavior fault id to be cleared.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| lease | [Lease](#bosdyn.api.Lease) | The Lease to show ownership of the robot. |
    +| behavior_fault_id | [uint32](#uint32) | Unique identifier for the error |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ClearBehaviorFaultResponse
    +
    +A ClearBehaviorFault response message has status indicating whether the service cleared
    +the fault or not.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| lease_use_result | [LeaseUseResult](#bosdyn.api.LeaseUseResult) | Details about how the lease was used. |
    +| status | [ClearBehaviorFaultResponse.Status](#bosdyn.api.ClearBehaviorFaultResponse.Status) | Return status for a request. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RobotCommand
    +
    +A command for a robot to execute.
    +The server decides if a set of commands is valid for a given robot and configuration.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| full_body_command | [FullBodyCommand.Request](#bosdyn.api.FullBodyCommand.Request) | Commands which require control of entire robot. |
    +| synchronized_command | [SynchronizedCommand.Request](#bosdyn.api.SynchronizedCommand.Request) | A synchronized command, for partial or full control of robot. |
    +| mobility_command | [MobilityCommand.Request](#bosdyn.api.MobilityCommand.Request) | Deprecation Warning *** DEPRECATED as of 2.1.0: A mobility command for a robot to execute. The following fields will be deprecated and moved to 'reserved' in a future release. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RobotCommandFeedback
    +
    +Command specific feedback. Distance to goal, estimated time remaining, probability of
    +success, etc. Note that the feedback should directly mirror the command request.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| full_body_feedback | [FullBodyCommand.Feedback](#bosdyn.api.FullBodyCommand.Feedback) | Commands which require control of entire robot. |
    +| synchronized_feedback | [SynchronizedCommand.Feedback](#bosdyn.api.SynchronizedCommand.Feedback) | A synchronized command, for partial or full control of robot. |
    +| mobility_feedback | [MobilityCommand.Feedback](#bosdyn.api.MobilityCommand.Feedback) | Deprecation Warning *** DEPRECATED as of 2.1.0: Command to control mobility system of a robot. The following fields will be deprecated and moved to 'reserved' in a future release. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RobotCommandFeedbackRequest
    +
    +The RobotCommandFeedback request message, which can get the feedback for a specific
    +robot command id number.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| robot_command_id | [uint32](#uint32) | Unique identifier for the command, provided by StartRequest. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RobotCommandFeedbackResponse
    +
    +The RobotCommandFeedback response message, which contains the progress of the robot command.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| lease_use_result | [LeaseUseResult](#bosdyn.api.LeaseUseResult) | Details about how the lease was used. |
    +| status | [RobotCommandFeedbackResponse.Status](#bosdyn.api.RobotCommandFeedbackResponse.Status) | DEPRECATED as of 2.1.0: General status whether or not command is still processing. |
    +| message | [string](#string) | DEPRECATED as of 2.1.0: Human-readable status message. Not for programmatic analysis. |
    +| feedback | [RobotCommandFeedback](#bosdyn.api.RobotCommandFeedback) | Command specific feedback. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RobotCommandRequest
    +
    +A RobotCommand request message includes the lease and command as well as a clock
    +identifier to ensure timesync when issuing commands with a fixed length.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| lease | [Lease](#bosdyn.api.Lease) | The Lease to show ownership of the robot. |
    +| command | [RobotCommand](#bosdyn.api.RobotCommand) | A command for a robot to execute. A command can be comprised of several subcommands. |
    +| clock_identifier | [string](#string) | Identifier provided by the time sync service to verify time sync between robot and client. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RobotCommandResponse
    +
    +The RobotCommand response message contains a robot command id that can be used to poll the
    +robot command service for feedback on the state of the command.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| lease_use_result | [LeaseUseResult](#bosdyn.api.LeaseUseResult) | Details about how the lease was used. |
    +| status | [RobotCommandResponse.Status](#bosdyn.api.RobotCommandResponse.Status) | Return status for a request. |
    +| message | [string](#string) | Human-readable error description. Not for programmatic analysis. |
    +| robot_command_id | [uint32](#uint32) | Unique identifier for the command, If empty, command was not accepted. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### ClearBehaviorFaultResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | An unknown / unexpected error occurred. |
    +| STATUS_CLEARED | 1 | The BehaviorFault has been cleared. |
    +| STATUS_NOT_CLEARED | 2 | The BehaviorFault could not be cleared. |
    +
    +
    +
    +
    +
    +### RobotCommandFeedbackResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Status enum is DEPRECATED as of 2.1.0. Behavior execution is in an unknown / unexpected state. |
    +| STATUS_PROCESSING | 1 | Status enum is DEPRECATED as of 2.1.0. The robot is actively working on the command |
    +| STATUS_COMMAND_OVERRIDDEN | 2 | Status enum is DEPRECATED as of 2.1.0. The command was replaced by a new command |
    +| STATUS_COMMAND_TIMED_OUT | 3 | Status enum is DEPRECATED as of 2.1.0. The command expired |
    +| STATUS_ROBOT_FROZEN | 4 | Status enum is DEPRECATED as of 2.1.0. The robot is in an unsafe state, and will only respond to known safe commands. |
    +
    +
    +
    +
    +
    +### RobotCommandResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | An unknown / unexpected error occurred. |
    +| STATUS_OK | 1 | Request was accepted. |
    +| STATUS_INVALID_REQUEST | 2 | [Programming Error] Request was invalid / malformed in some way. |
    +| STATUS_UNSUPPORTED | 3 | [Programming Error] The robot does not understand this command. |
    +| STATUS_NO_TIMESYNC | 4 | [Timesync Error] Client has not done timesync with robot. |
    +| STATUS_EXPIRED | 5 | [Timesync Error] The command was received after its end_time had already passed. |
    +| STATUS_TOO_DISTANT | 6 | [Timesync Error] The command end time was too far in the future. |
    +| STATUS_NOT_POWERED_ON | 7 | [Hardware Error] The robot must be powered on to accept a command. |
    +| STATUS_BEHAVIOR_FAULT | 9 | [Robot State Error] The robot must not have behavior faults. |
    +| STATUS_UNKNOWN_FRAME | 8 | [Frame Error] The frame_name for a command was not a known frame. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# robot_command_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### RobotCommandService
    +
    +The robot command service allows a client application to control and move the robot.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| RobotCommand | [RobotCommandRequest](#bosdyn.api.RobotCommandRequest) | [RobotCommandResponse](#bosdyn.api.RobotCommandResponse) | Starts a behavior command on the robot. Issuing a new command overrides the active command. Each command is issued a UID for feedback retrieval. |
    +| RobotCommandFeedback | [RobotCommandFeedbackRequest](#bosdyn.api.RobotCommandFeedbackRequest) | [RobotCommandFeedbackResponse](#bosdyn.api.RobotCommandFeedbackResponse) | A client queries this RPC to determine a robot's progress towards completion of a command. This updates the client with metrics like "distance to goal." The client should use this feedback to determine whether the current command has succeeeded or failed, and thus send the next command. |
    +| ClearBehaviorFault | [ClearBehaviorFaultRequest](#bosdyn.api.ClearBehaviorFaultRequest) | [ClearBehaviorFaultResponse](#bosdyn.api.ClearBehaviorFaultResponse) | Clear robot behavior fault. |
    +
    + 
    +
    +
    +
    +
    +
    +# robot_id.proto
    +
    +
    +
    +
    +
    +### RobotId
    +
    +Robot identity information, which should be static while robot is powered-on.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| serial_number | [string](#string) | A unique string identifier for the particular robot. |
    +| species | [string](#string) | Type of robot. E.g., 'spot'. |
    +| version | [string](#string) | Robot version/platform. |
    +| software_release | [RobotSoftwareRelease](#bosdyn.api.RobotSoftwareRelease) | Version information about software running on the robot. |
    +| nickname | [string](#string) | Optional, customer-supplied nickname. |
    +| computer_serial_number | [string](#string) | Computer Serial Number. Unlike serial_number, which identifies a complete robot, the computer_serial_number identifies the computer hardware used in the robot. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RobotIdRequest
    +
    +The RobotId request message sent to a robot to learn it's basic identification information.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request/response header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RobotIdResponse
    +
    +The RobotId response message, including the ID information for a robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common request/response header. |
    +| robot_id | [RobotId](#bosdyn.api.RobotId) | The requested RobotId information. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RobotSoftwareRelease
    +
    +Description of the software release currently running on the robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| version | [SoftwareVersion](#bosdyn.api.SoftwareVersion) | The software version, e.g., 2.0.1 |
    +| name | [string](#string) | The name of the robot, e.g., '20190601' |
    +| type | [string](#string) | Kind of software release. |
    +| changeset_date | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Timestamp of the changeset. |
    +| changeset | [string](#string) | Changeset hash. |
    +| api_version | [string](#string) | API version. E.g., 2.14.5. |
    +| build_information | [string](#string) | Extra information associated with the build. |
    +| install_date | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Date/time when release was installed. |
    +| parameters | [Parameter](#bosdyn.api.Parameter) | Other information about the build. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SoftwareVersion
    +
    +The software versioning number for a release.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| major_version | [int32](#int32) | Signficant changes to software. |
    +| minor_version | [int32](#int32) | Normal changes to software. |
    +| patch_level | [int32](#int32) | Fixes which should not change intended capabilities or affect compatibility. |
    +
    +
    +
    +
    +
    + 
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# robot_id_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### RobotIdService
    +
    +RobotIdService provides mostly static identifying information about a robot.
    +User authentication is not required to access RobotIdService to assist with
    +early robot discovery.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| GetRobotId | [RobotIdRequest](#bosdyn.api.RobotIdRequest) | [RobotIdResponse](#bosdyn.api.RobotIdResponse) | Get the robot id information. The ID contains basic information about a robot which is made available over the network as part of robot discovery without requiring user authentication. |
    +
    + 
    +
    +
    +
    +
    +
    +# robot_state.proto
    +
    +
    +
    +
    +
    +### BatteryState
    +
    +The battery state for the robot. This includes information about the charge or the
    +battery temperature.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Robot clock timestamp corresponding to these readings. |
    +| identifier | [string](#string) | An identifier for this battery (could be a serial number or a name. subject to change). |
    +| charge_percentage | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Number from 0 (empty) to 100 (full) indicating the estimated state of charge of the battery. |
    +| estimated_runtime | [google.protobuf.Duration](#google.protobuf.Duration) | An estimate of remaining runtime. Note that this field might not be populated. |
    +| current | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Measured current into (charging, positive) or out of (discharging, negative) the battery in Amps. |
    +| voltage | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Measured voltage of the entire battery in Volts. |
    +| temperatures | [double](#double) | Measured temperature measurements of battery, in Celsius. Temperatures may be measured in many locations across the battery. |
    +| status | [BatteryState.Status](#bosdyn.api.BatteryState.Status) | Current state of the battery. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### BehaviorFault
    +
    +The details of what the behavior fault consistents of, and the id for the fault. The unique
    +behavior_fault_id can be used to clear the fault in robot command service ClearBehaviorFault rpc.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| behavior_fault_id | [uint32](#uint32) | Behavior fault unique id |
    +| onset_timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Time of robot local clock at time of the error |
    +| cause | [BehaviorFault.Cause](#bosdyn.api.BehaviorFault.Cause) | The potential cause of the fault. |
    +| status | [BehaviorFault.Status](#bosdyn.api.BehaviorFault.Status) | Information about the status/what can be done with the fault. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### BehaviorFaultState
    +
    +This describes any current behaviror faults on the robot, which would block any robot commands
    +from going through. These can be cleared using the ClearBehaviorFault rpc in the robot command
    +service.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| faults | [BehaviorFault](#bosdyn.api.BehaviorFault) | Current errors potentially blocking commands on robot |
    +
    +
    +
    +
    +
    +
    +
    +
    +### CommsState
    +
    +The current comms information, including what comms the robot is using and the current status
    +of the comms network.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Robot timestamp corresponding to these readings. |
    +| wifi_state | [WiFiState](#bosdyn.api.WiFiState) | The communication state is WiFi. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### EStopState
    +
    +The robot's current E-Stop states and endpoints.
    +A typical robot has several different E-Stops, all which must be "NOT_ESTOPPED"
    +in order to run the robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Robot clock timestamp corresponding to these readings. |
    +| name | [string](#string) | Name of the E-Stop |
    +| type | [EStopState.Type](#bosdyn.api.EStopState.Type) | What kind of E-Stop this message describes. |
    +| state | [EStopState.State](#bosdyn.api.EStopState.State) | The state of the E-Stop (is it E-Stopped or not?) |
    +| state_description | [string](#string) | Optional description of E-Stop status. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### FootState
    +
    +Information about the foot positions and contact state, on a per-foot basis.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| foot_position_rt_body | [Vec3](#bosdyn.api.Vec3) | The foot position described relative to the body. |
    +| contact | [FootState.Contact](#bosdyn.api.FootState.Contact) | Is the foot in contact with the ground? |
    +| terrain | [FootState.TerrainState](#bosdyn.api.FootState.TerrainState) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### FootState.TerrainState
    +
    +Foot specific terrain data. Data may not be valid if the contact state is
    +not CONTACT_MADE.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| ground_mu_est | [double](#double) | Estimated ground coefficient of friction for this foot. |
    +| frame_name | [string](#string) | Reference frame name for vector data. |
    +| foot_slip_distance_rt_frame | [Vec3](#bosdyn.api.Vec3) | Foot slip distance rt named frame |
    +| foot_slip_velocity_rt_frame | [Vec3](#bosdyn.api.Vec3) | Foot slip velocity rt named frame |
    +| ground_contact_normal_rt_frame | [Vec3](#bosdyn.api.Vec3) | Ground contact normal rt named frame |
    +| visual_surface_ground_penetration_mean | [double](#double) | Mean penetration (meters) of the foot below the ground visual surface. For penetrable terrains (gravel/sand/grass etc.) these values are positive. Negative values would indicate potential odometry issues. |
    +| visual_surface_ground_penetration_std | [double](#double) | Standard deviation of the visual surface ground penetration. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### HardwareConfiguration
    +
    +Robot Hardware Configuration, described with the robot skeleton.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| skeleton | [Skeleton](#bosdyn.api.Skeleton) | Robot link and joint description. |
    +| can_power_command_request_off_robot | [bool](#bool) | Turn off the robot. Same as physical switch. |
    +| can_power_command_request_cycle_robot | [bool](#bool) | Power cycle the robot. Same as physical switch. |
    +| can_power_command_request_payload_ports | [bool](#bool) | Control power to the payload ports. |
    +| can_power_command_request_wifi_radio | [bool](#bool) | Control power to the hardware Wi-Fi radio. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### JointState
    +
    +Proto containing the state of a joint on the robot. This can be used with the robot skeleton to
    +update the current view of the robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| name | [string](#string) | This name maps directly to the joints in the URDF. |
    +| position | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | This is typically an angle in radians as joints are typically revolute. However, for translational joints this could be a distance in meters. |
    +| velocity | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The joint velocity in [m/s]. |
    +| acceleration | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | The joint acceleration in [m/s^2]. |
    +| load | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | This is typically a torque in Newton meters as joints are typically revolute. However, for translational joints this could be a force in Newtons. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### KinematicState
    +
    +The kinematic state of the robot describes the current estimated positions of the robot body and joints throughout the world.
    +It includes a transform snapshot of the robot’s current known frames as well as joint states and the velocity of the body.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| joint_states | [JointState](#bosdyn.api.JointState) | Joint state of all robot joints. |
    +| acquisition_timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Robot clock timestamp corresponding to these readings. |
    +| transforms_snapshot | [FrameTreeSnapshot](#bosdyn.api.FrameTreeSnapshot) | A tree-based collection of transformations, which will include the transformations to the robot body ("body") in addition to transformations to the common frames ("world", "dr") and ground plane estimate "gpe". All transforms within the snapshot are at the acquisition time of kinematic state. |
    +| velocity_of_body_in_vision | [SE3Velocity](#bosdyn.api.SE3Velocity) | Velocity of the body frame with respect to vision frame and expressed in vision frame. The linear velocity is applied at the origin of the body frame. |
    +| velocity_of_body_in_odom | [SE3Velocity](#bosdyn.api.SE3Velocity) | Velocity of the body frame with respect to odom frame and expressed in odom frame. Again, the linear velocity is applied at the origin of the body frame. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ManipulatorState
    +
    +Additional state published if an arm is attached to the robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| gripper_open_percentage | [double](#double) | How open the gripper is, measured in percent. 0 = fully closed, 100 = fully open. |
    +| is_gripper_holding_item | [bool](#bool) | Will be true if the gripper is holding an item, false otherwise. |
    +| estimated_end_effector_force_in_hand | [Vec3](#bosdyn.api.Vec3) | The estimated force on the end-effector expressed in the hand frame. |
    +| stow_state | [ManipulatorState.StowState](#bosdyn.api.ManipulatorState.StowState) | Information on if the arm is stowed, or deployed. |
    +| velocity_of_hand_in_vision | [SE3Velocity](#bosdyn.api.SE3Velocity) | Velocity of the hand frame with respect to vision frame and expressed in vision frame. The linear velocity is applied at the origin of the hand frame. |
    +| velocity_of_hand_in_odom | [SE3Velocity](#bosdyn.api.SE3Velocity) | Velocity of the hand frame with respect to odom frame and expressed in odom frame. Again, the linear velocity is applied at the origin of the hand frame. |
    +| carry_state | [ManipulatorState.CarryState](#bosdyn.api.ManipulatorState.CarryState) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PowerState
    +
    +The power state for the robot.
    +If a robot is not in the POWER OFF state, if is not safe to approach.
    +The robot must not be E-Stopped to enter the POWER_ON state.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Robot clock timestamp corresponding to these readings. |
    +| motor_power_state | [PowerState.MotorPowerState](#bosdyn.api.PowerState.MotorPowerState) | The motor power state of the robot. |
    +| shore_power_state | [PowerState.ShorePowerState](#bosdyn.api.PowerState.ShorePowerState) | The shore power state of the robot. |
    +| robot_power_state | [PowerState.RobotPowerState](#bosdyn.api.PowerState.RobotPowerState) | The payload ports power state of the robot. |
    +| payload_ports_power_state | [PowerState.PayloadPortsPowerState](#bosdyn.api.PowerState.PayloadPortsPowerState) | The payload ports power state of the robot. |
    +| wifi_radio_power_state | [PowerState.WifiRadioPowerState](#bosdyn.api.PowerState.WifiRadioPowerState) | The hardware radio power state of the robot. |
    +| locomotion_charge_percentage | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Number from 0 (empty) to 100 (full) indicating the estimated state of charge. This field provides a summary of the BatteryStates that provide power for motor and/or base compute power, both of which are required for locomotion. |
    +| locomotion_estimated_runtime | [google.protobuf.Duration](#google.protobuf.Duration) | An estimate of remaining runtime. Note that this field might not be populated. This field provides a summary of the BatteryStates that provide power for motor and/or base compute power, both of which are required for locomotion. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RobotHardwareConfigurationRequest
    +
    +The RobotHardwareConfiguration request message to get hardware configuration, described
    +by the robot skeleton and urdf.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RobotHardwareConfigurationResponse
    +
    +The RobotHardwareConfiguration response message, which returns the hardware config from the time
    +the request was received.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| hardware_configuration | [HardwareConfiguration](#bosdyn.api.HardwareConfiguration) | The requested RobotState. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RobotImpairedState
    +
    +Keeps track of why the robot is not able to drive autonomously.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| impaired_status | [RobotImpairedState.ImpairedStatus](#bosdyn.api.RobotImpairedState.ImpairedStatus) | If the status is ROBOT_IMPAIRED, this is specifically why the robot is impaired. |
    +| system_faults | [SystemFault](#bosdyn.api.SystemFault) | If impaired_status is STATUS_SYSTEM_FAULT, these are the faults which caused the robot to stop. |
    +| service_faults | [ServiceFault](#bosdyn.api.ServiceFault) | If impaired_status is STATUS_SERVICE_FAULT, these are the service faults which caused the robot to stop. |
    +| behavior_faults | [BehaviorFault](#bosdyn.api.BehaviorFault) | If impaired_status is STATUS_BEHAVIOR_FAULT, these are the behavior faults which caused the robot to stop. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RobotLinkModelRequest
    +
    +The RobotLinkModel request message uses a link name returned by the RobotHardwareConfiguration response to
    +get the associated OBJ file.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| link_name | [string](#string) | The link name of which the OBJ file shoould represent. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RobotLinkModelResponse
    +
    +The RobotLinkModel response message returns the OBJ file for a specifc robot link.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| link_model | [Skeleton.Link.ObjModel](#bosdyn.api.Skeleton.Link.ObjModel) | The requested RobotState skeleton obj model. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RobotMetrics
    +
    +Key robot metrics (e.g., Gait cycles (count), distance walked, time moving, etc...).
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Robot timestamp corresponding to these metrics. |
    +| metrics | [Parameter](#bosdyn.api.Parameter) | Key tracked robot metrics, such as distance walked, runtime, etc. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RobotMetricsRequest
    +
    +The RobotMetrics request message to get metrics and parameters from the robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RobotMetricsResponse
    +
    +The RobotMetrics response message, which returns the metrics information from the time
    +the request was received.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| robot_metrics | [RobotMetrics](#bosdyn.api.RobotMetrics) | The requested robot metrics. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RobotState
    +
    +The current state of the robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| power_state | [PowerState](#bosdyn.api.PowerState) | Power state (e.g. motor power). |
    +| battery_states | [BatteryState](#bosdyn.api.BatteryState) | Battery state (e.g. charge, temperature, current). |
    +| comms_states | [CommsState](#bosdyn.api.CommsState) | Communication state (e.g. type of comms network). |
    +| system_fault_state | [SystemFaultState](#bosdyn.api.SystemFaultState) | Different system faults for the robot. |
    +| estop_states | [EStopState](#bosdyn.api.EStopState) | Because there may be multiple E-Stops, and because E-Stops may be supplied with payloads, this is a repeated field instead of a hardcoded list. |
    +| kinematic_state | [KinematicState](#bosdyn.api.KinematicState) | Kinematic state of the robot (e.g. positions, velocities, other frame information). |
    +| behavior_fault_state | [BehaviorFaultState](#bosdyn.api.BehaviorFaultState) | Robot behavior fault state. |
    +| foot_state | [FootState](#bosdyn.api.FootState) | The foot states (and contact information). |
    +| manipulator_state | [ManipulatorState](#bosdyn.api.ManipulatorState) | State of the manipulator, only populated if an arm is attached to the robot. |
    +| service_fault_state | [ServiceFaultState](#bosdyn.api.ServiceFaultState) | Service faults for services registered with the robot. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RobotStateRequest
    +
    +The RobotState request message to get the current state of the robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RobotStateResponse
    +
    +The RobotState response message, which returns the robot state information from the time
    +the request was received.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| robot_state | [RobotState](#bosdyn.api.RobotState) | The requested RobotState. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ServiceFaultState
    +
    +The current state of each service fault the robot is experiencing.
    +An "active" fault indicates a fault currently in a service.
    +A "historical" fault indicates a, now cleared, service problem.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| faults | [ServiceFault](#bosdyn.api.ServiceFault) | Currently active faults |
    +| historical_faults | [ServiceFault](#bosdyn.api.ServiceFault) | Service faults that have been cleared. Acts as a ring buffer with size of 50. |
    +| aggregated | [ServiceFaultState.AggregatedEntry](#bosdyn.api.ServiceFaultState.AggregatedEntry) | Aggregated service fault data. Maps attribute string to highest severity level of any active fault containing that attribute string. This provides a very quick way of determining if there any "locomotion" or "vision" faults above a certain severity level. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ServiceFaultState.AggregatedEntry
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| key | [string](#string) |  |
    +| value | [ServiceFault.Severity](#bosdyn.api.ServiceFault.Severity) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Skeleton
    +
    +Kinematic model of the robot skeleton.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| links | [Skeleton.Link](#bosdyn.api.Skeleton.Link) | The list of links that make up the robot skeleton. |
    +| urdf | [string](#string) | URDF description of the robot skeleton. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Skeleton.Link
    +
    +Each link of the robot skeleton.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| name | [string](#string) | The link name, which matches those used in the urdf. |
    +| obj_model | [Skeleton.Link.ObjModel](#bosdyn.api.Skeleton.Link.ObjModel) | The OBJ file representing the model of this link. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Skeleton.Link.ObjModel
    +
    +Model to draw, expressed as an obj file.
    +Note: To limit the size of responses, obj_file_contents might be omitted.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| file_name | [string](#string) | Name of the file. |
    +| file_contents | [string](#string) | The contents of the file. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SystemFault
    +
    +The current system faults for a robot.
    +A fault is an indicator of a hardware or software problem on the robot. An
    +active fault may indicate the robot may fail to comply with a user request.
    +The exact response a fault may vary, but possible responses include: failure
    +to enable motor power, loss of perception enabled behavior, or triggering a
    +fault recovery behavior on robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| name | [string](#string) | Name of the fault. |
    +| onset_timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Time of robot local clock at fault onset. |
    +| duration | [google.protobuf.Duration](#google.protobuf.Duration) | Time elapsed since onset of the fault. |
    +| code | [int32](#int32) | Error code returned by a fault. The exact interpretation of the fault code is unique to each variety of fault on the robot. The code is useful for Boston Dynamics support staff to diagnose hardware/software issues on robot. |
    +| uid | [uint64](#uint64) | Fault UID |
    +| error_message | [string](#string) | User visible description of the fault (and possibly remedies.) |
    +| attributes | [string](#string) | Fault attributes Each fault may be flagged with attribute metadata (strings in this case.) These attributes are useful to communicate that a particular fault may have significant effect on robot operations. Some potential attributes may be "robot", "imu", "vision", or "battery". These attributes would let us flag a fault as indicating a problem with the base robot hardware, gyro, perception system, or battery respectively. A fault may have, zero, one, or more attributes attached to it, i.e. a "battery" fault may also be considered a "robot" fault. |
    +| severity | [SystemFault.Severity](#bosdyn.api.SystemFault.Severity) | Fault severity, how bad is the fault? The severity level will have some indication of the potential robot response to the fault. For example, a fault marked with "battery" attribute and severity level SEVERITY_WARN may indicate a low battery state of charge. However a "battery" fault with severity level SEVERITY_CRITICAL likely means the robot is going to shutdown immediately. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SystemFaultState
    +
    +The current state of each system fault the robot is experiencing.
    +An "active" fault indicates a hardware/software currently on the robot.
    +A "historical" fault indicates a, now cleared, hardware/software problem.
    +Historical faults are useful to diagnose robot behavior subject to intermittent failed states.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| faults | [SystemFault](#bosdyn.api.SystemFault) | Currently active faults |
    +| historical_faults | [SystemFault](#bosdyn.api.SystemFault) | Inactive faults that cleared within the last 10 minutes |
    +| aggregated | [SystemFaultState.AggregatedEntry](#bosdyn.api.SystemFaultState.AggregatedEntry) | Aggregated fault data. This provides a very quick way of determining if there any "battery" or "vision" faults above a certain severity level. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SystemFaultState.AggregatedEntry
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| key | [string](#string) |  |
    +| value | [SystemFault.Severity](#bosdyn.api.SystemFault.Severity) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### WiFiState
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| current_mode | [WiFiState.Mode](#bosdyn.api.WiFiState.Mode) | Current WiFi mode. |
    +| essid | [string](#string) | Essid of robot (master mode) or connected network. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### BatteryState.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | The battery is in an unknown / unexpected state. |
    +| STATUS_MISSING | 1 | The battery is not plugged in or otherwise not talking. |
    +| STATUS_CHARGING | 2 | The battery is plugged in to shore power and charging. |
    +| STATUS_DISCHARGING | 3 | The battery is not plugged into shore power and discharging. |
    +| STATUS_BOOTING | 4 | The battery was just plugged in and is booting up= 3; |
    +
    +
    +
    +
    +
    +### BehaviorFault.Cause
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| CAUSE_UNKNOWN | 0 | Unknown cause of error |
    +| CAUSE_FALL | 1 | Error caused by mobility failure or fall |
    +| CAUSE_HARDWARE | 2 | Error caused by robot hardware malfunction |
    +| CAUSE_LEASE_TIMEOUT | 3 | A lease has timed out |
    +
    +
    +
    +
    +
    +### BehaviorFault.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Unknown clearable status |
    +| STATUS_CLEARABLE | 1 | Fault is clearable |
    +| STATUS_UNCLEARABLE | 2 | Fault is currently not clearable |
    +
    +
    +
    +
    +
    +### EStopState.State
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATE_UNKNOWN | 0 | No E-Stop information is present. Only happens in an error case. |
    +| STATE_ESTOPPED | 1 | E-Stop is active -- robot cannot power its actuators. |
    +| STATE_NOT_ESTOPPED | 2 | E-Stop is released -- robot may be able to power its actuators. |
    +
    +
    +
    +
    +
    +### EStopState.Type
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| TYPE_UNKNOWN | 0 | Unknown type of E-Stop. Do not use this field. |
    +| TYPE_HARDWARE | 1 | E-Stop is a physical button |
    +| TYPE_SOFTWARE | 2 | E-Stop is a software process |
    +
    +
    +
    +
    +
    +### FootState.Contact
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| CONTACT_UNKNOWN | 0 | Unknown contact. Do not use. |
    +| CONTACT_MADE | 1 | The foot is currently in contact with the ground. |
    +| CONTACT_LOST | 2 | The foot is not in contact with the ground. |
    +
    +
    +
    +
    +
    +### ManipulatorState.CarryState
    +
    +The stowing behavior is modified as a function of the Carry State.  If holding an item, the
    +stowing behavior will be modified as follows:
    + NOT_CARRIABLE - The arm will not stow, instead entering stop
    + CARRIABLE - The arm will not stow, instead entering stop
    + CARRIABLE_AND_STOWABLE - The arm will stow while continuing to grasp the item
    +The comms loss behavior of the arm is also modified as follows:
    + NOT_CARRIABLE - The arm will release the item and stow
    + CARRIABLE - The arm will not stow, instead entering stop
    + CARRIABLE_AND_STOWABLE - The arm will stow while continuing to grasp the item
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| CARRY_STATE_UNKNOWN | 0 |  |
    +| CARRY_STATE_NOT_CARRIABLE | 1 |  |
    +| CARRY_STATE_CARRIABLE | 2 |  |
    +| CARRY_STATE_CARRIABLE_AND_STOWABLE | 3 |  |
    +
    +
    +
    +
    +
    +### ManipulatorState.StowState
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STOWSTATE_UNKNOWN | 0 |  |
    +| STOWSTATE_STOWED | 1 |  |
    +| STOWSTATE_DEPLOYED | 2 |  |
    +
    +
    +
    +
    +
    +### PowerState.MotorPowerState
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATE_UNKNOWN | 0 | Unknown motor power state. Do not use this field. |
    +| MOTOR_POWER_STATE_UNKNOWN | 0 |  |
    +| STATE_OFF | 1 | Motors are off, the robot is safe to approach. |
    +| MOTOR_POWER_STATE_OFF | 1 |  |
    +| STATE_ON | 2 | The motors are powered. |
    +| MOTOR_POWER_STATE_ON | 2 |  |
    +| STATE_POWERING_ON | 3 | The robot has received an ON command, and is turning on. |
    +| MOTOR_POWER_STATE_POWERING_ON | 3 |  |
    +| STATE_POWERING_OFF | 4 | In the process of powering down, not yet safe to approach. |
    +| MOTOR_POWER_STATE_POWERING_OFF | 4 |  |
    +| STATE_ERROR | 5 | The robot is in an error state and must be powered off before attempting to re-power. |
    +| MOTOR_POWER_STATE_ERROR | 5 |  |
    +
    +
    +
    +
    +
    +### PowerState.PayloadPortsPowerState
    +
    +State describing if the payload port has power.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| PAYLOAD_PORTS_POWER_STATE_UNKNOWN | 0 | Unknown payload port power state. Do not use this field. |
    +| PAYLOAD_PORTS_POWER_STATE_ON | 1 | The payload port is powered on. |
    +| PAYLOAD_PORTS_POWER_STATE_OFF | 2 | The payload port does not have power. |
    +
    +
    +
    +
    +
    +### PowerState.RobotPowerState
    +
    +State describing if the robot has power.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| ROBOT_POWER_STATE_UNKNOWN | 0 | Unknown robot power state. Do not use this field. |
    +| ROBOT_POWER_STATE_ON | 1 | The robot is powered on. |
    +| ROBOT_POWER_STATE_OFF | 2 | The robot does not have power. Impossible to get this response, as the robot cannot respond if it is powered off. |
    +
    +
    +
    +
    +
    +### PowerState.ShorePowerState
    +
    +State describing if robot is connected to shore (wall) power. Robot can't be powered on
    +while on shore power
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATE_UNKNOWN_SHORE_POWER | 0 | Unknown shore power state. Do not use. |
    +| SHORE_POWER_STATE_UNKNOWN | 0 |  |
    +| STATE_ON_SHORE_POWER | 1 | The robot is connected to shore power. The robot will not power on while connected to shore power. |
    +| SHORE_POWER_STATE_ON | 1 |  |
    +| STATE_OFF_SHORE_POWER | 2 | The robot is disconnected from shore power and motors can be powered up. |
    +| SHORE_POWER_STATE_OFF | 2 |  |
    +
    +
    +
    +
    +
    +### PowerState.WifiRadioPowerState
    +
    +State describing if the robot Wi-Fi router has power.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| WIFI_RADIO_POWER_STATE_UNKNOWN | 0 | Unknown radio power state. Do not use this field. |
    +| WIFI_RADIO_POWER_STATE_ON | 1 | The radio is powered on. |
    +| WIFI_RADIO_POWER_STATE_OFF | 2 | The radio does not have power. |
    +
    +
    +
    +
    +
    +### RobotImpairedState.ImpairedStatus
    +
    +If the robot is stopped due to being impaired, this is the reason why.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| IMPAIRED_STATUS_UNKNOWN | 0 | Unknown/unexpected error. |
    +| IMPAIRED_STATUS_OK | 1 | The robot is able to drive. |
    +| IMPAIRED_STATUS_NO_ROBOT_DATA | 2 | The autonomous system does not have any data from the robot state service. |
    +| IMPAIRED_STATUS_SYSTEM_FAULT | 3 | There is a system fault which caused the robot to stop. See system_fault for details. |
    +| IMPAIRED_STATUS_NO_MOTOR_POWER | 4 | The robot's motors are not powered on. |
    +| IMPAIRED_STATUS_REMOTE_CLOUDS_NOT_WORKING | 5 | The autonomous system is expected to have a remote point cloud (e.g. a LIDAR), but this is not working. |
    +| IMPAIRED_STATUS_SERVICE_FAULT | 6 | A remote service the autonomous system depends on is not working. |
    +| IMPAIRED_STATUS_BEHAVIOR_FAULT | 7 | A behavior fault caused the robot to stop. See behavior_faults for details. |
    +
    +
    +
    +
    +
    +### SystemFault.Severity
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| SEVERITY_UNKNOWN | 0 | Unknown severity |
    +| SEVERITY_INFO | 1 | No hardware problem |
    +| SEVERITY_WARN | 2 | Robot performance may be degraded |
    +| SEVERITY_CRITICAL | 3 | Critical fault |
    +
    +
    +
    +
    +
    +### WiFiState.Mode
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| MODE_UNKNOWN | 0 | The robot's comms state is unknown, or no user requested mode. |
    +| MODE_ACCESS_POINT | 1 | The robot is acting as an access point. |
    +| MODE_CLIENT | 2 | The robot is connected to a network. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# robot_state_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### RobotStateService
    +
    +The robot state service tracks all information about the measured and computed states of the robot at the current time.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| GetRobotState | [RobotStateRequest](#bosdyn.api.RobotStateRequest) | [RobotStateResponse](#bosdyn.api.RobotStateResponse) | Get robot state information (such as kinematic state, power state, or faults). |
    +| GetRobotMetrics | [RobotMetricsRequest](#bosdyn.api.RobotMetricsRequest) | [RobotMetricsResponse](#bosdyn.api.RobotMetricsResponse) | Get different robot metrics and parameters from the robot. |
    +| GetRobotHardwareConfiguration | [RobotHardwareConfigurationRequest](#bosdyn.api.RobotHardwareConfigurationRequest) | [RobotHardwareConfigurationResponse](#bosdyn.api.RobotHardwareConfigurationResponse) | Get the hardware configuration of the robot, which describes the robot skeleton and urdf. |
    +| GetRobotLinkModel | [RobotLinkModelRequest](#bosdyn.api.RobotLinkModelRequest) | [RobotLinkModelResponse](#bosdyn.api.RobotLinkModelResponse) | Returns the OBJ file for a specifc robot link. Intended to be called after GetRobotHardwareConfiguration, using the link names returned by that call. |
    +
    + 
    +
    +
    +
    +
    +
    +# service_fault.proto
    +
    +
    +
    +
    +
    +### ClearServiceFaultRequest
    +
    +Clear a service fault from the robot's ServiceFaultState (in robot_state.proto).
    +The active ServiceFault to clear will be determined by matching fault_name and
    +service_name/payload_guid, specified in the ServiceFaultId message.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| fault_id | [ServiceFaultId](#bosdyn.api.ServiceFaultId) | Identifying information of the fault to clear. |
    +| clear_all_service_faults | [bool](#bool) | Clear all faults that are associated with the same service_name. Use carefully. |
    +| clear_all_payload_faults | [bool](#bool) | Clear all faults that are associated with the same payload_guid. Use carefully. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ClearServiceFaultResponse
    +
    +The ClearServiceFault response message contains a header indicating success.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [ClearServiceFaultResponse.Status](#bosdyn.api.ClearServiceFaultResponse.Status) | Return status for the request. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ServiceFault
    +
    +The current service faults for services registered with the robot.
    +A fault is an indicator of a problem with a service or payload registered
    +with the robot. An active fault may indicate a service may fail to comply
    +with a user request.
    +If the name, service_name, and payload_guid of a newly triggered ServiceFault matches an
    +already active ServiceFault the new fault will not be added to the active fault list.
    +The oldest matching fault will be maintained.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| fault_id | [ServiceFaultId](#bosdyn.api.ServiceFaultId) | Identifying information of the fault. |
    +| error_message | [string](#string) | User visible description of the fault (and possibly remedies). Will be displayed on tablet. |
    +| attributes | [string](#string) | Fault attributes Each fault may be flagged with attribute metadata (strings in this case.) These attributes are useful to communicate that a particular fault may have significant effect on the operations of services. Some potential attributes may be "autowalk", "Spot CORE", "vision", or "gauge detection". These attributes enable the caller to flag a fault as indicating a problem with particular robot abstractions. A fault may have, zero, one, or more attributes attached to it. |
    +| severity | [ServiceFault.Severity](#bosdyn.api.ServiceFault.Severity) | The severity level will have some indication of the potential breakage resulting from the fault. For example, a fault associated with payload X and severity level SEVERITY_INFO may indicate the payload is in an unexpected state but still operational. However, a fault with serverity level SEVERITY_CRITICAL may indicate the payload is no longer operational. |
    +| onset_timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Time of robot local clock at fault onset. Set by robot-state service. |
    +| duration | [google.protobuf.Duration](#google.protobuf.Duration) | Time elapsed since onset of the fault. Set by robot-state service. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ServiceFaultId
    +
    +Information necessary to uniquely specify a service fault.
    +A service fault typically is associated with a service running on the robot or a
    +payload. Occassionally, the fault may pertain to a payload but no specific service
    +on the payload. In that situation, no service_name should not be specified and instead
    +a payload_guid should be specified. Otherwise, in the standard case of a service
    +originating fault, only the service_name should be specified and the payload_guid
    +will be populated automatically by the fault service on robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| fault_name | [string](#string) | Name of the fault. |
    +| service_name | [string](#string) | Name of the registered service associated with the fault. Optional. Service name does not need to be specified if this is a payload-level fault with no associated service. |
    +| payload_guid | [string](#string) | GUID of the payload associated with the faulted service. Optional. If not set, it will be assigned to the payload associated with the service_name. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### TriggerServiceFaultRequest
    +
    +Trigger a new service fault that will be reported in the robot ServiceFaultState.
    +These faults will be displayed in the tablet. Developers should be careful to
    +avoid overwhelming operators with dozens of minor messages.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| fault | [ServiceFault](#bosdyn.api.ServiceFault) | The fault to report in ServiceFaultState. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### TriggerServiceFaultResponse
    +
    +The TriggerServiceFault response message contains a header indicating success.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [TriggerServiceFaultResponse.Status](#bosdyn.api.TriggerServiceFaultResponse.Status) | Return status for the request. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### ClearServiceFaultResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | UNKNOWN should never be used. |
    +| STATUS_OK | 1 | Success. The fault has been cleared. |
    +| STATUS_FAULT_NOT_ACTIVE | 2 | ServiceFaultId not found in active faults. |
    +
    +
    +
    +
    +
    +### ServiceFault.Severity
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| SEVERITY_UNKNOWN | 0 | Unknown severity |
    +| SEVERITY_INFO | 1 | Service still functional |
    +| SEVERITY_WARN | 2 | Service performance may be degraded |
    +| SEVERITY_CRITICAL | 3 | Critical service fault |
    +
    +
    +
    +
    +
    +### TriggerServiceFaultResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | UNKNOWN should never be used. |
    +| STATUS_OK | 1 | Success. The fault has been triggerd. |
    +| STATUS_FAULT_ALREADY_ACTIVE | 2 | ServiceFaultId already in active faults. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# sparse_features.proto
    +
    +
    +
    +
    +
    +### Keypoint
    +
    +A point of interest in an image expressed as a pixel coordinate with associated metadata.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| coordinates | [Vec2](#bosdyn.api.Vec2) | The image pixel coordinates of the keypoint. |
    +| binary_descriptor | [bytes](#bytes) | A binary descriptor representing the keypoint. |
    +| score | [float](#float) | The score of this keypoint from the underlying keypoint detector, if applicable. |
    +| size | [float](#float) | The diameter in pixels of the local neighborhood used to construct the descriptor. |
    +| angle | [float](#float) | The orientation of the keypoint, if applicable. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### KeypointMatches
    +
    +A pair of keypoint sets containing only features in common that have been matched.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| reference_keypoints | [KeypointSet](#bosdyn.api.KeypointSet) | The set of common keypoints in a first ("reference") image. |
    +| live_keypoints | [KeypointSet](#bosdyn.api.KeypointSet) | The set of common keypoints in a second ("live") image. |
    +| matches | [Match](#bosdyn.api.Match) | Indices of pairs of matches in the two KeypointSets and their distance measure. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### KeypointSet
    +
    +A set of keypoints detected in a single image.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| keypoints | [Keypoint](#bosdyn.api.Keypoint) | A set of detected keypoints and associated metadata. |
    +| type | [KeypointSet.KeypointType](#bosdyn.api.KeypointSet.KeypointType) | The algorithm used to compute this keypoint and its descriptor. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Match
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| reference_index | [int32](#int32) | The index in the reference KeypointSet of the keypoint in the matching pair. |
    +| live_index | [int32](#int32) | The index in the live KeypointSet of the keypoint in the matching pair. |
    +| distance | [float](#float) | The distance in descriptor space between the two keypoints. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### KeypointSet.KeypointType
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| KEYPOINT_UNKNOWN | 0 |  |
    +| KEYPOINT_SIMPLE | 1 | Keypoints that consist only of image coordinates. Simple keypoints do not have descriptors. |
    +| KEYPOINT_ORB | 2 | Keypoints detected by the ORB feature extraction algorithm (Oriented FAST and Rotated BRIEF.) |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# spot/door.proto
    +
    +
    +
    +
    +
    +### DoorCommand
    +
    +Door Command specific request and Feedback.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### DoorCommand.AutoGraspCommand
    +
    +The robot searches along a ray for the door handle and automatically grasp it before
    +executing door opening.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| frame_name | [string](#string) | The name of the frame that the following fields are expressed in. |
    +| search_ray_start_in_frame | [bosdyn.api.Vec3](#bosdyn.api.Vec3) | The start of the ray the robot searches along for the door handle. |
    +| search_ray_end_in_frame | [bosdyn.api.Vec3](#bosdyn.api.Vec3) | The end of the ray the robot searches along for the door handle. |
    +| hinge_side | [DoorCommand.HingeSide](#bosdyn.api.spot.DoorCommand.HingeSide) | The side of the hinge with respect to the robot when facing the door. |
    +| swing_direction | [DoorCommand.SwingDirection](#bosdyn.api.spot.DoorCommand.SwingDirection) | The direction the door moves with respect to the robot. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DoorCommand.AutoPushCommand
    +
    +Open doors that do not require a grasp, just a push. This could be a door with no latching
    +mechanism that just requires a push, or a door with a pushbar.
    +The robot will automatically push the door open and walk through.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| frame_name | [string](#string) | The name of the frame that the following fields are expressed in. |
    +| push_point_in_frame | [bosdyn.api.Vec3](#bosdyn.api.Vec3) | The point that the robot will push on. |
    +| hinge_side | [DoorCommand.HingeSide](#bosdyn.api.spot.DoorCommand.HingeSide) | The side of the hinge with respect to the robot when facing the door. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DoorCommand.Feedback
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| status | [DoorCommand.Feedback.Status](#bosdyn.api.spot.DoorCommand.Feedback.Status) | Current status of the command. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DoorCommand.Request
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| auto_grasp_command | [DoorCommand.AutoGraspCommand](#bosdyn.api.spot.DoorCommand.AutoGraspCommand) |  |
    +| warmstart_command | [DoorCommand.WarmstartCommand](#bosdyn.api.spot.DoorCommand.WarmstartCommand) |  |
    +| auto_push_command | [DoorCommand.AutoPushCommand](#bosdyn.api.spot.DoorCommand.AutoPushCommand) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DoorCommand.WarmstartCommand
    +
    +The robot is already grasping the door handle and will continue opening the door based on
    +user specified params.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| hinge_side | [DoorCommand.HingeSide](#bosdyn.api.spot.DoorCommand.HingeSide) | The side of the hinge with respect to the robot when facing the door. |
    +| swing_direction | [DoorCommand.SwingDirection](#bosdyn.api.spot.DoorCommand.SwingDirection) | The direction the door moves with respect to the robot. |
    +| handle_type | [DoorCommand.HandleType](#bosdyn.api.spot.DoorCommand.HandleType) | The type of handle on the door. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### OpenDoorCommandRequest
    +
    +A door command for the robot to execute plus a lease.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| lease | [bosdyn.api.Lease](#bosdyn.api.Lease) | The Lease to show ownership of the robot. |
    +| door_command | [DoorCommand.Request](#bosdyn.api.spot.DoorCommand.Request) | The command to execute. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### OpenDoorCommandResponse
    +
    +Response to the door command request.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| lease_use_result | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | Details about how the lease was used. |
    +| status | [OpenDoorCommandResponse.Status](#bosdyn.api.spot.OpenDoorCommandResponse.Status) | Return status for a request. |
    +| message | [string](#string) | Human-readable error description. Not for programmatic analysis. |
    +| door_command_id | [uint32](#uint32) | Unique identifier for the command, If empty, command was not accepted. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### OpenDoorFeedbackRequest
    +
    +A request for feedback of a specific door command.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| door_command_id | [uint32](#uint32) | Unique identifier for the command, provided by OpenDoorResponse. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### OpenDoorFeedbackResponse
    +
    +Feedback for a specific door command. This RPC reports the robot's progress opening a door.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [bosdyn.api.RobotCommandFeedbackStatus.Status](#bosdyn.api.RobotCommandFeedbackStatus.Status) | Generic robot command feedback. |
    +| feedback | [DoorCommand.Feedback](#bosdyn.api.spot.DoorCommand.Feedback) | Specific door full body command feedback. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### DoorCommand.Feedback.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | STATUS_UNKNOWN should never be used. If used, an internal error has happened. |
    +| STATUS_COMPLETED | 1 | Robot has finished opening the door. |
    +| STATUS_IN_PROGRESS | 2 | Robot is attempting to open the door. |
    +
    +
    +
    +
    +
    +### DoorCommand.HandleType
    +
    +Specify type of door handle.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| HANDLE_TYPE_UNKNOWN | 0 |  |
    +| HANDLE_TYPE_LEVER | 1 |  |
    +| HANDLE_TYPE_KNOB | 2 |  |
    +| HANDLE_TYPE_FIXED_GRASP | 3 |  |
    +
    +
    +
    +
    +
    +### DoorCommand.HingeSide
    +
    +Specify if the hinge is on the left or right side of the door, when looking at the door,
    +relative to the door handle.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| HINGE_SIDE_UNKNOWN | 0 |  |
    +| HINGE_SIDE_LEFT | 1 |  |
    +| HINGE_SIDE_RIGHT | 2 |  |
    +
    +
    +
    +
    +
    +### DoorCommand.SwingDirection
    +
    +Specify if the door is push or pull, when looking at the door.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| SWING_DIRECTION_UNKNOWN | 0 |  |
    +| SWING_DIRECTION_INSWING | 1 |  |
    +| SWING_DIRECTION_OUTSWING | 2 |  |
    +
    +
    +
    +
    +
    +### OpenDoorCommandResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | An unknown / unexpected error occurred. |
    +| STATUS_OK | 1 | Request was accepted. |
    +| STATUS_ROBOT_COMMAND_ERROR | 2 | Error sending command to RobotCommandService. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# spot/door_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### DoorService
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| OpenDoor | [OpenDoorCommandRequest](#bosdyn.api.spot.OpenDoorCommandRequest) | [OpenDoorCommandResponse](#bosdyn.api.spot.OpenDoorCommandResponse) |  |
    +| OpenDoorFeedback | [OpenDoorFeedbackRequest](#bosdyn.api.spot.OpenDoorFeedbackRequest) | [OpenDoorFeedbackResponse](#bosdyn.api.spot.OpenDoorFeedbackResponse) |  |
    +
    + 
    +
    +
    +
    +
    +
    +# spot/robot_command.proto
    +
    +
    +
    +
    +
    +### BodyControlParams
    +
    +Parameters for offsetting the body from the normal default.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| base_offset_rt_footprint | [bosdyn.api.SE3Trajectory](#bosdyn.api.SE3Trajectory) | Desired base offset relative to the footprint pseudo-frame. The footprint pseudo-frame is a gravity aligned frame with its origin located at the geometric center of the feet in the X-Y axis, and at the nominal height of the hips in the Z axis. The yaw of the frame (wrt the world) is calcuated by the average foot locations, and is aligned with the feet. |
    +| rotation_setting | [BodyControlParams.RotationSetting](#bosdyn.api.spot.BodyControlParams.RotationSetting) | The rotation setting for the robot body. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### BodyExternalForceParams
    +
    +External Force on robot body parameters. This is a beta feature and still can have some odd behaviors.
    +By default, the external force estimator is disabled on the robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| external_force_indicator | [BodyExternalForceParams.ExternalForceIndicator](#bosdyn.api.spot.BodyExternalForceParams.ExternalForceIndicator) | The type of external force described by the parameters. |
    +| frame_name | [string](#string) | The frame name for which the external_force_override is defined in. The frame must be known to the robot. |
    +| external_force_override | [bosdyn.api.Vec3](#bosdyn.api.Vec3) | Specifies a force that the body should expect to feel. This allows the robot to "lean into" an external force. Be careful using this override, since incorrect information can cause the robot to fall over. For example, if the robot is leaning against a wall in front of it, the force override would be in the negative x dimension. If the robot was pulling something directly behind it, the force override would be in the negative x dimension as well. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### MobilityParams
    +
    +Params common across spot movement and mobility.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| vel_limit | [bosdyn.api.SE2VelocityLimit](#bosdyn.api.SE2VelocityLimit) | Max allowable velocity at any point in trajectory. |
    +| body_control | [BodyControlParams](#bosdyn.api.spot.BodyControlParams) | Parameters for controlling Spot's body during motion. |
    +| locomotion_hint | [LocomotionHint](#bosdyn.api.spot.LocomotionHint) | Desired gait during locomotion |
    +| stair_hint | [bool](#bool) | Stairs are only supported in trot gaits. Using this hint will override some user defaults in order to optimize stair behavior. |
    +| allow_degraded_perception | [bool](#bool) | Allow the robot to move with degraded perception when there are perception faults. |
    +| obstacle_params | [ObstacleParams](#bosdyn.api.spot.ObstacleParams) | Control of obstacle avoidance. |
    +| swing_height | [SwingHeight](#bosdyn.api.spot.SwingHeight) | Swing height setting |
    +| terrain_params | [TerrainParams](#bosdyn.api.spot.TerrainParams) | Ground terrain parameters. |
    +| disallow_stair_tracker | [bool](#bool) | Prevent the robot from using StairTracker even if in stairs mode. |
    +| external_force_params | [BodyExternalForceParams](#bosdyn.api.spot.BodyExternalForceParams) | Robot Body External Force parameters |
    +| disallow_non_stairs_pitch_limiting | [bool](#bool) | Prevent the robot from pitching to get a better look at rearward terrain except in stairs mode. |
    +| disable_nearmap_cliff_avoidance | [bool](#bool) | Disable the secondary nearmap-based cliff avoidance that runs while on stairs. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ObstacleParams
    +
    +Parameters for obstacle avoidance types.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| disable_vision_foot_obstacle_avoidance | [bool](#bool) | Use vision to make the feet avoid obstacles by swinging higher? |
    +| disable_vision_foot_constraint_avoidance | [bool](#bool) | Use vision to make the feet avoid constraints like edges of stairs? |
    +| disable_vision_body_obstacle_avoidance | [bool](#bool) | Use vision to make the body avoid obstacles? |
    +| obstacle_avoidance_padding | [double](#double) | Desired padding around the body to use when attempting to avoid obstacles. Described in meters. Must be >= 0. |
    +| disable_vision_foot_obstacle_body_assist | [bool](#bool) | Prevent the robot body from raising above nominal height to avoid lower-leg collisions with the terrain. |
    +| disable_vision_negative_obstacles | [bool](#bool) | Use vision to make the robot avoid stepping into negative obstacles? |
    +
    +
    +
    +
    +
    +
    +
    +
    +### TerrainParams
    +
    +Ground contact parameters that describe the terrain.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| ground_mu_hint | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Terrain coefficient of friction user hint. This value must be postive and will clamped if necessary on the robot side. Best suggested values lie in the range between 0.4 and 0.8 (which is the robot's default.) |
    +| enable_grated_floor | [bool](#bool) | Deprecation Warning *** DEPRECATED as of 3.0.0: The boolean field has been replaced by the field grated_surfaces_mode The following field will be deprecated and moved to 'reserved' in a future release. |
    +| grated_surfaces_mode | [TerrainParams.GratedSurfacesMode](#bosdyn.api.spot.TerrainParams.GratedSurfacesMode) | The selected option for grated surfaces mode |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### BodyControlParams.RotationSetting
    +
    +Setting for how the robot interprets base offset pitch & roll components.
    +In the default case (ROTATION_SETTING_OFFSET) the robot will naturally align the body to the pitch of the current terrain.
    +In some circumstances, the user may wish to override this value and try to maintain alignment
    +with respect to gravity. Be careful with this setting as it may likely degrade robot performance in
    +complex terrain, e.g. stairs, platforms, or slopes of sufficiently high grade.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| ROTATION_SETTING_UNKNOWN | 0 | Invalid; do not use. |
    +| ROTATION_SETTING_OFFSET | 1 | Pitch & Roll are offset with respect to orientation of the footprint. |
    +| ROTATION_SETTING_ABSOLUTE | 2 | Pitch & Roll are offset with respect to gravity. |
    +
    +
    +
    +
    +
    +### BodyExternalForceParams.ExternalForceIndicator
    +
    +Indicates what external force estimate/override the robot should use.
    +By default, the external force estimator is disabled on the robot.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| EXTERNAL_FORCE_NONE | 0 | No external forces considered. |
    +| EXTERNAL_FORCE_USE_ESTIMATE | 1 | Use external forces estimated by the robot |
    +| EXTERNAL_FORCE_USE_OVERRIDE | 2 | Use external forces specified in an override vector. |
    +
    +
    +
    +
    +
    +### LocomotionHint
    +
    +The locomotion hint specifying the gait of the robot.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| HINT_UNKNOWN | 0 | Invalid; do not use. |
    +| HINT_AUTO | 1 | No hint, robot chooses an appropriate gait (typically trot.) |
    +| HINT_TROT | 2 | Most robust gait which moves diagonal legs together. |
    +| HINT_SPEED_SELECT_TROT | 3 | Trot which comes to a stand when not commanded to move. |
    +| HINT_CRAWL | 4 | Slow and steady gait which moves only one foot at a time. |
    +| HINT_SPEED_SELECT_CRAWL | 10 | Crawl which comes to a stand when not commanded to move. |
    +| HINT_AMBLE | 5 | Four beat gait where one foot touches down at a time. |
    +| HINT_SPEED_SELECT_AMBLE | 6 | Amble which comes to a stand when not commanded to move. |
    +| HINT_JOG | 7 | Demo gait which moves diagonal leg pairs together with an aerial phase. |
    +| HINT_HOP | 8 | Demo gait which hops while holding some feet in the air. |
    +| HINT_AUTO_TROT | 3 | HINT_AUTO_TROT is deprecated due to the name being too similar to the Spot Autowalk feature. It has been replaced by HINT_SPEED_SELECT_TROT. Keeping this value in here for now for backwards compatibility, but this may be removed in future releases. |
    +| HINT_AUTO_AMBLE | 6 | HINT_AUTO_AMBLE is deprecated due to the name being too similar to the Spot Autowalk feature. It has been replaced by HINT_SPEED_SELECT_AMBLE. Keeping this value in here for now for backwards compatibility, but this may be removed in future releases. |
    +
    +
    +
    +
    +
    +### SwingHeight
    +
    +The type of swing height for a step.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| SWING_HEIGHT_UNKNOWN | 0 | Invalid; do not use. |
    +| SWING_HEIGHT_LOW | 1 | Low-stepping. Robot will try to only swing legs a few cm away from ground. |
    +| SWING_HEIGHT_MEDIUM | 2 | Default for most cases, use other values with caution. |
    +| SWING_HEIGHT_HIGH | 3 | High-stepping. Possibly useful with degraded vision operation. |
    +
    +
    +
    +
    +
    +### TerrainParams.GratedSurfacesMode
    +
    +Options for Grated Surfaces Mode. When Grated Surfaces Mode is on, the robot assumes the
    +ground below it is made of grated metal or other repeated pattern. When on, the robot will
    +make assumptions about the environment structure and more aggressively filter noise in
    +perception data.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| GRATED_SURFACES_MODE_UNKNOWN | 0 | Invalid; do not use. |
    +| GRATED_SURFACES_MODE_OFF | 1 |  |
    +| GRATED_SURFACES_MODE_ON | 2 |  |
    +| GRATED_SURFACES_MODE_AUTO | 3 | Robot will automatically turn mode on or off |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# spot/spot_check.proto
    +
    +
    +
    +
    +
    +### CameraCalibrationCommandRequest
    +
    +Request for the CameraCalibrationCommand service.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| lease | [bosdyn.api.Lease](#bosdyn.api.Lease) | The Lease to show ownership of the robot. Lease is required for all cal commands. |
    +| command | [CameraCalibrationCommandRequest.Command](#bosdyn.api.spot.CameraCalibrationCommandRequest.Command) | Command to start/stop the calibration. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### CameraCalibrationCommandResponse
    +
    +Response for the CameraCalibrationCommand service.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| lease_use_result | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | Details about how the lease was used. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### CameraCalibrationFeedbackRequest
    +
    +Request for the CameraCalibrationFeedback service.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### CameraCalibrationFeedbackResponse
    +
    +Response for the CameraCalibrationFeedback service.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [CameraCalibrationFeedbackResponse.Status](#bosdyn.api.spot.CameraCalibrationFeedbackResponse.Status) | Status of camera calibration procedure. |
    +| progress | [float](#float) | The approximate progress of the calibration routine, range [0-1]. Status takes precedence over progress value. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DepthPlaneSpotCheckResult
    +
    +Results from camera check.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| status | [DepthPlaneSpotCheckResult.Status](#bosdyn.api.spot.DepthPlaneSpotCheckResult.Status) | Return status for the request. |
    +| severity_score | [float](#float) | Higher is worse. Above 100 means the camera is severely out of calibration. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### FootHeightCheckResult
    +
    +Results from foot height checks.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| status | [FootHeightCheckResult.Status](#bosdyn.api.spot.FootHeightCheckResult.Status) | Return status for the request. |
    +| foot_height_error_from_mean | [float](#float) | The difference between foot height and mean feet height (m). |
    +
    +
    +
    +
    +
    +
    +
    +
    +### HipRangeOfMotionResult
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| error | [HipRangeOfMotionResult.Error](#bosdyn.api.spot.HipRangeOfMotionResult.Error) |  |
    +| hx | [float](#float) | The measured angles (radians) of the HX and HY joints where the obstruction was detected |
    +| hy | [float](#float) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### JointKinematicCheckResult
    +
    +Kinematic calibration results
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| error | [JointKinematicCheckResult.Error](#bosdyn.api.spot.JointKinematicCheckResult.Error) | A flag to indicate if results has an error. |
    +| offset | [float](#float) | The current offset [rad] |
    +| old_offset | [float](#float) | The previous offset [rad] |
    +| health_score | [float](#float) | Joint calibration health score. range [0-1] 0 indicates an unhealthy kinematic joint calibration 1 indicates a perfect kinematic joint calibration Typically, values greater than 0.8 should be expected. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### LegPairCheckResult
    +
    +Results from leg pair checks..
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| status | [LegPairCheckResult.Status](#bosdyn.api.spot.LegPairCheckResult.Status) | Return status for the request. |
    +| leg_pair_distance_change | [float](#float) | The change in estimated distance between two feet from tall to short stand (m) |
    +
    +
    +
    +
    +
    +
    +
    +
    +### LoadCellSpotCheckResult
    +
    +Results from load cell check.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| error | [LoadCellSpotCheckResult.Error](#bosdyn.api.spot.LoadCellSpotCheckResult.Error) | A flag to indicate if results has an error. |
    +| zero | [float](#float) | The current loadcell zero as fraction of full range [0-1] |
    +| old_zero | [float](#float) | The previous loadcell zero as fraction of full range [0-1] |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PayloadCheckResult
    +
    +Results of payload check.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| error | [PayloadCheckResult.Error](#bosdyn.api.spot.PayloadCheckResult.Error) | A flag to indicate if configuration has an error. |
    +| extra_payload | [float](#float) | Indicates how much extra payload (in kg) we think the robot has Positive indicates robot has more payload than it is configured. Negative indicates robot has less payload than it is configured. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SpotCheckCommandRequest
    +
    +Request for the SpotCheckCommand service.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| lease | [bosdyn.api.Lease](#bosdyn.api.Lease) | The Lease to show ownership of the robot. Lease required to issue any SpotCheck command. |
    +| command | [SpotCheckCommandRequest.Command](#bosdyn.api.spot.SpotCheckCommandRequest.Command) | The describing what the spot check service should do. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SpotCheckCommandResponse
    +
    +Response for the SpotCheckCommand service.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| lease_use_result | [bosdyn.api.LeaseUseResult](#bosdyn.api.LeaseUseResult) | Details about how the lease was used. |
    +| status | [SpotCheckCommandResponse.Status](#bosdyn.api.spot.SpotCheckCommandResponse.Status) | Command status |
    +| message | [string](#string) | Human-readable description if an error occurred. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SpotCheckFeedbackRequest
    +
    +Request for the SpotCheckFeedback service.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SpotCheckFeedbackResponse
    +
    +Response for the SpotCheckFeedback service.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| state | [SpotCheckFeedbackResponse.State](#bosdyn.api.spot.SpotCheckFeedbackResponse.State) | The state of the spot check routine. |
    +| last_command | [SpotCheckCommandRequest.Command](#bosdyn.api.spot.SpotCheckCommandRequest.Command) | The last command executed by Spotcheck. When SpotCheck is in state WAITING_FOR_COMMAND, the last command has completed. |
    +| error | [SpotCheckFeedbackResponse.Error](#bosdyn.api.spot.SpotCheckFeedbackResponse.Error) | The specifics of the error for the SpotCheck service. |
    +| camera_results | [SpotCheckFeedbackResponse.CameraResultsEntry](#bosdyn.api.spot.SpotCheckFeedbackResponse.CameraResultsEntry) | Results from camera check. The key string is the location of the camera (e.g. frontright, frontleft, left, ...) |
    +| load_cell_results | [SpotCheckFeedbackResponse.LoadCellResultsEntry](#bosdyn.api.spot.SpotCheckFeedbackResponse.LoadCellResultsEntry) | Results from load cell calibration. The key string is the location of the joint (e.g. fl.hxa, fl.hya, fl.kna, ...) |
    +| kinematic_cal_results | [SpotCheckFeedbackResponse.KinematicCalResultsEntry](#bosdyn.api.spot.SpotCheckFeedbackResponse.KinematicCalResultsEntry) | Results from output position sensor calibration. The key string is the location of the joint (e.g. fl.hx, fl.hy, fl.kn, ...) |
    +| payload_result | [PayloadCheckResult](#bosdyn.api.spot.PayloadCheckResult) | Result from the payload check |
    +| foot_height_results | [SpotCheckFeedbackResponse.FootHeightResultsEntry](#bosdyn.api.spot.SpotCheckFeedbackResponse.FootHeightResultsEntry) | Deprecated. Results of foot height validation. The key string is the name of the leg (e.g. fl, fr, hl, ...) |
    +| leg_pair_results | [SpotCheckFeedbackResponse.LegPairResultsEntry](#bosdyn.api.spot.SpotCheckFeedbackResponse.LegPairResultsEntry) | Deprecated. Results of leg pair validation. The key string is the name of the leg pair (e.g. fl-fr, fl-hl, ...) |
    +| hip_range_of_motion_results | [SpotCheckFeedbackResponse.HipRangeOfMotionResultsEntry](#bosdyn.api.spot.SpotCheckFeedbackResponse.HipRangeOfMotionResultsEntry) | Results of the hip range of motion check The key string is the name of the leg (e.g. fl, fr, hl, ...) |
    +| progress | [float](#float) | The approximate progress of the spot check routine, range [0-1]. |
    +| last_cal_timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Timestamp for the most up-to-date calibration |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SpotCheckFeedbackResponse.CameraResultsEntry
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| key | [string](#string) |  |
    +| value | [DepthPlaneSpotCheckResult](#bosdyn.api.spot.DepthPlaneSpotCheckResult) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SpotCheckFeedbackResponse.FootHeightResultsEntry
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| key | [string](#string) |  |
    +| value | [FootHeightCheckResult](#bosdyn.api.spot.FootHeightCheckResult) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SpotCheckFeedbackResponse.HipRangeOfMotionResultsEntry
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| key | [string](#string) |  |
    +| value | [HipRangeOfMotionResult](#bosdyn.api.spot.HipRangeOfMotionResult) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SpotCheckFeedbackResponse.KinematicCalResultsEntry
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| key | [string](#string) |  |
    +| value | [JointKinematicCheckResult](#bosdyn.api.spot.JointKinematicCheckResult) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SpotCheckFeedbackResponse.LegPairResultsEntry
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| key | [string](#string) |  |
    +| value | [LegPairCheckResult](#bosdyn.api.spot.LegPairCheckResult) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SpotCheckFeedbackResponse.LoadCellResultsEntry
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| key | [string](#string) |  |
    +| value | [LoadCellSpotCheckResult](#bosdyn.api.spot.LoadCellSpotCheckResult) |  |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### CameraCalibrationCommandRequest.Command
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| COMMAND_UNKNOWN | 0 | Unused enum. |
    +| COMMAND_START | 1 | Start calibration routine. |
    +| COMMAND_CANCEL | 2 | Cancel calibration routine. |
    +
    +
    +
    +
    +
    +### CameraCalibrationFeedbackResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Unused enum. |
    +| STATUS_PROCESSING | 1 | The robot is actively running calibration routine. |
    +| STATUS_SUCCESS | 2 | The robot successfully ran calibration routine and is ready to use again. |
    +| STATUS_USER_CANCELED | 3 | API client canceled calibration. |
    +| STATUS_POWER_ERROR | 4 | The robot is not powered on. |
    +| STATUS_LEASE_ERROR | 5 | Ownership error during calibration. |
    +| STATUS_ROBOT_COMMAND_ERROR | 7 | Robot encountered an error while trying to move around the calibration target. Robot possibly encountered a fault. Check robot state for more details |
    +| STATUS_CALIBRATION_ERROR | 8 | Calibration procedure produced an invalid result. This may occur in poor lighting conditions or if calibration target moved during calibration procedure. |
    +| STATUS_INTERNAL_ERROR | 9 | Something extraordinary happened. Try power cycling robot or contact BD. |
    +| STATUS_CAMERA_FOCUS_ERROR | 14 | Camera focus issue detected. This is a hardware issue. |
    +| STATUS_TARGET_NOT_CENTERED | 6 | Target partially, but not fully, in view when starting calibration. |
    +| STATUS_TARGET_NOT_IN_VIEW | 11 | Target not visible when starting calibration. |
    +| STATUS_TARGET_NOT_GRAVITY_ALIGNED | 12 | Target not aligned with gravity when starting calibration. |
    +| STATUS_TARGET_UPSIDE_DOWN | 13 | Target upside down when starting calibration. |
    +| STATUS_NEVER_RUN | 10 | Calibration routine has never been run. No feedback to give. |
    +| STATUS_CAMERA_NOT_DETECTED | 15 | One of the cameras is not detected on the USB bus. |
    +| STATUS_INTRINSIC_WRITE_FAILED | 16 | Failed to write intrinsic calibration. |
    +| STATUS_EXTRINSIC_WRITE_FAILED | 17 | Failed to write extrinsic calibration. |
    +
    +
    +
    +
    +
    +### DepthPlaneSpotCheckResult.Status
    +
    +Errors reflect an issue with camera hardware.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Unused enum. |
    +| STATUS_OK | 1 | No detected calibration error. |
    +| STATUS_WARNING | 2 | Possible calibration error detected. |
    +| STATUS_ERROR | 3 | Error with robot calibration. |
    +
    +
    +
    +
    +
    +### FootHeightCheckResult.Status
    +
    +Errors reflect an issue with robot calibration.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Unused enum. |
    +| STATUS_OK | 1 | No detected calibration error. |
    +| STATUS_WARNING | 2 | Possible calibration error detected. |
    +| STATUS_ERROR | 3 | Error with robot calibration. |
    +
    +
    +
    +
    +
    +### HipRangeOfMotionResult.Error
    +
    +Errors reflect an issue with hip range of motion
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| ERROR_UNKNOWN | 0 |  |
    +| ERROR_NONE | 1 |  |
    +| ERROR_OBSTRUCTED | 2 |  |
    +
    +
    +
    +
    +
    +### JointKinematicCheckResult.Error
    +
    +Errors reflect an issue with robot hardware.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| ERROR_UNKNOWN | 0 | Unused enum. |
    +| ERROR_NONE | 1 | No hardware error detected. |
    +| ERROR_CLUTCH_SLIP | 2 | Error detected in clutch performance. |
    +
    +
    +
    +
    +
    +### LegPairCheckResult.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Unused enum. |
    +| STATUS_OK | 1 | No detected calibration error. |
    +| STATUS_WARNING | 2 | Possible calibration error detected. |
    +| STATUS_ERROR | 3 | Error with robot calibration. |
    +
    +
    +
    +
    +
    +### LoadCellSpotCheckResult.Error
    +
    +Errors reflect an issue with robot hardware.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| ERROR_UNKNOWN | 0 | Unused enum. |
    +| ERROR_NONE | 1 | No hardware error detected. |
    +| ERROR_ZERO_OUT_OF_RANGE | 2 | Load cell calibration failure. |
    +
    +
    +
    +
    +
    +### PayloadCheckResult.Error
    +
    +Errors reflect an issue with payload configuration.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| ERROR_UNKNOWN | 0 | Unused enum. |
    +| ERROR_NONE | 1 | No error found in the payloads. |
    +| ERROR_MASS_DISCREPANCY | 2 | There is a mass discrepancy between the registered payload and what is estimated. |
    +
    +
    +
    +
    +
    +### SpotCheckCommandRequest.Command
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| COMMAND_UNKNOWN | 0 | Unused enum. |
    +| COMMAND_START | 1 | Start spot check joint calibration and camera checks. |
    +| COMMAND_ABORT | 2 | Abort spot check joint calibration and camera check. |
    +| COMMAND_REVERT_CAL | 3 | Revert joint calibration back to the previous values. |
    +
    +
    +
    +
    +
    +### SpotCheckCommandResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Unknown |
    +| STATUS_OK | 1 | Request was accepted. |
    +| STATUS_ERROR | 2 | An error ocurred. |
    +
    +
    +
    +
    +
    +### SpotCheckFeedbackResponse.Error
    +
    +If SpotCheck experienced an error, specific error details reported here.
    +This reflects an error in the routine.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| ERROR_UNKNOWN | 0 | Unused enum. |
    +| ERROR_NONE | 1 | No error has occurred. |
    +| ERROR_UNEXPECTED_POWER_CHANGE | 2 | Unexpected motor power state transition. |
    +| ERROR_INIT_IMU_CHECK | 3 | Robot body is not flat on the ground. |
    +| ERROR_INIT_NOT_SITTING | 4 | Robot body is not close to a sitting pose |
    +| ERROR_LOADCELL_TIMEOUT | 5 | Timeout during loadcell calibration. |
    +| ERROR_POWER_ON_FAILURE | 6 | Error enabling motor power. |
    +| ERROR_ENDSTOP_TIMEOUT | 7 | Timeout during endstop calibration. |
    +| ERROR_FAILED_STAND | 8 | Robot failed to stand. |
    +| ERROR_CAMERA_TIMEOUT | 9 | Timeout during camera check. |
    +| ERROR_GROUND_CHECK | 10 | Flat ground check failed. |
    +| ERROR_POWER_OFF_FAILURE | 11 | Robot failed to power off. |
    +| ERROR_REVERT_FAILURE | 12 | Robot failed to revert calibration. |
    +| ERROR_FGKC_FAILURE | 13 | Robot failed to do flat ground kinematic calibration. |
    +
    +
    +
    +
    +
    +### SpotCheckFeedbackResponse.State
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATE_UNKNOWN | 0 | Unused enum. |
    +| STATE_USER_ABORTED | 1 | SpotCheck is aborted by the user. |
    +| STATE_STARTING | 2 | SpotCheck is initializing. |
    +| STATE_LOADCELL_CAL | 3 | Load cell calibration underway. |
    +| STATE_ENDSTOP_CAL | 4 | Endstop calibration underway. |
    +| STATE_CAMERA_CHECK | 5 | Camera check underway. |
    +| STATE_BODY_POSING | 6 | Body pose routine underway. |
    +| STATE_FINISHED | 7 | Spot check successfully finished. |
    +| STATE_REVERTING_CAL | 8 | Reverting calibration to previous values. |
    +| STATE_ERROR | 9 | Error occurred while running spotcheck. Inspect error for more info. |
    +| STATE_WAITING_FOR_COMMAND | 10 | Waiting for user command. |
    +| STATE_HIP_RANGE_OF_MOTION_CHECK | 11 | Hip range of motion check underway. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# spot/spot_check_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### SpotCheckService
    +
    +RPCs for monitoring robot health and recalibration various sensors. These procedures should be
    +run periodically in order to keep the system running in the best possible condition.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| SpotCheckCommand | [SpotCheckCommandRequest](#bosdyn.api.spot.SpotCheckCommandRequest) | [SpotCheckCommandResponse](#bosdyn.api.spot.SpotCheckCommandResponse) | Send a command to the SpotCheck service. The spotcheck service is responsible to both recalibrating actuation sensors and checking camera health. |
    +| SpotCheckFeedback | [SpotCheckFeedbackRequest](#bosdyn.api.spot.SpotCheckFeedbackRequest) | [SpotCheckFeedbackResponse](#bosdyn.api.spot.SpotCheckFeedbackResponse) | Check the status of the spot check procedure. After procedure completes, this reports back results for specific joints and cameras. |
    +| CameraCalibrationCommand | [CameraCalibrationCommandRequest](#bosdyn.api.spot.CameraCalibrationCommandRequest) | [CameraCalibrationCommandResponse](#bosdyn.api.spot.CameraCalibrationCommandResponse) | Send a camera calibration command to the robot. Used to start or abort a calibration routine. |
    +| CameraCalibrationFeedback | [CameraCalibrationFeedbackRequest](#bosdyn.api.spot.CameraCalibrationFeedbackRequest) | [CameraCalibrationFeedbackResponse](#bosdyn.api.spot.CameraCalibrationFeedbackResponse) | Check the status of the camera calibration procedure. |
    +
    + 
    +
    +
    +
    +
    +
    +# spot_cam/LED.proto
    +
    +
    +
    +
    +
    +### GetLEDBrightnessRequest
    +
    +Request the current state of LEDs on the SpotCam.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetLEDBrightnessResponse
    +
    +Describes the current brightnesses of all LEDs.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| brightnesses | [float](#float) | Brightness [0, 1] of the LED located at indices [0, 3]. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetLEDBrightnessRequest
    +
    +Set individual LED brightnesses.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| brightnesses | [SetLEDBrightnessRequest.BrightnessesEntry](#bosdyn.api.spot_cam.SetLEDBrightnessRequest.BrightnessesEntry) | Brightness [0, 1] to assign to the LED located at indices [0, 3]. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetLEDBrightnessRequest.BrightnessesEntry
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| key | [int32](#int32) |  |
    +| value | [float](#float) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetLEDBrightnessResponse
    +
    +Response with any errors setting LED brightnesses.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +
    +
    +
    +
    +
    + 
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# spot_cam/audio.proto
    +
    +
    +
    +
    +
    +### DeleteSoundRequest
    +
    +Remove a loaded sound from the library of loaded sounds.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| sound | [Sound](#bosdyn.api.spot_cam.Sound) | The sound identifier as uploaded by LoadSoundRequest or listed in ListSoundsResponse. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DeleteSoundResponse
    +
    +Result of deleting a sound from the library.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetAudioCaptureChannelRequest
    +
    +Request to get the audio capture channel
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetAudioCaptureChannelResponse
    +
    +Result of getting the audio capture channel
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) |  |
    +| channel | [AudioCaptureChannel](#bosdyn.api.spot_cam.AudioCaptureChannel) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetAudioCaptureGainRequest
    +
    +Request to get the audio capture channel
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) |  |
    +| channel | [AudioCaptureChannel](#bosdyn.api.spot_cam.AudioCaptureChannel) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetAudioCaptureGainResponse
    +
    +Result of getting the audio capture gain
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) |  |
    +| gain | [double](#double) | Gain for microphone, range from 0.0 to 1.0 |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetVolumeRequest
    +
    +Query the current volume level of the system.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetVolumeResponse
    +
    +Provides the current volume level of the system.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| volume | [float](#float) | volume, as a percentage of maximum. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListSoundsRequest
    +
    +Request for all sounds present on the robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListSoundsResponse
    +
    +List of all sounds present on the robot.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| sounds | [Sound](#bosdyn.api.spot_cam.Sound) | All sounds currently loaded. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### LoadSoundRequest
    +
    +Load a new sound onto the robot for future playback.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| sound | [Sound](#bosdyn.api.spot_cam.Sound) | Identifier for the sound. If the same identifier is used as a previously loaded sound, that sound will be overwritten with the new data. |
    +| data | [bosdyn.api.DataChunk](#bosdyn.api.DataChunk) | WAV bytes to be joined. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### LoadSoundResponse
    +
    +Result of uploading a sound.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PlaySoundRequest
    +
    +Begin playing a loaded sound from the robot's speakers.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| sound | [Sound](#bosdyn.api.spot_cam.Sound) | The sound identifier as uploaded by LoadSoundRequest or listed in ListSoundsResponse. |
    +| gain | [google.protobuf.FloatValue](#google.protobuf.FloatValue) | If the gain field is populated, then volume of the sound is multiplied by this value. Does not modify the system volume level. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PlaySoundResponse
    +
    +Result of staring playback of a sound.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetAudioCaptureChannelRequest
    +
    +Request to set the audio capture channel
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) |  |
    +| channel | [AudioCaptureChannel](#bosdyn.api.spot_cam.AudioCaptureChannel) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetAudioCaptureChannelResponse
    +
    +Result of setting the audio capture channel
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetAudioCaptureGainRequest
    +
    +Request to set the audio capture channel
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) |  |
    +| channel | [AudioCaptureChannel](#bosdyn.api.spot_cam.AudioCaptureChannel) |  |
    +| gain | [double](#double) | Gain for microphone, range from 0.0 to 1.0 |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetAudioCaptureGainResponse
    +
    +Result of setting the audio capture gain
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetVolumeRequest
    +
    +Set the desired volume level of the system.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| volume | [float](#float) | volume, as a percentage of maximum. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetVolumeResponse
    +
    +Result of changing the system volume level.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Sound
    +
    +Identifier for a playable sound.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| name | [string](#string) | internally, sounds are stored in a flat table. This name is the identifier of a sound effect |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### AudioCaptureChannel
    +
    +Audio capture channel
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| AUDIO_CHANNEL_UNKNOWN | 0 |  |
    +| AUDIO_CHANNEL_INTERNAL_MIC | 1 |  |
    +| AUDIO_CHANNEL_EXTERNAL_MIC | 2 |  |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# spot_cam/camera.proto
    +
    +
    +
    +
    +
    +### Camera
    +
    +Description of the details of a particular camera.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| name | [string](#string) | Identifier for the camera. |
    +| resolution | [bosdyn.api.Vec2](#bosdyn.api.Vec2) | Resolution of the sensor, where x = width and y = height. |
    +| base_frame_name | [string](#string) | The frame name for the image sensor source. This frame will show up in the FrameTreeSnapshot grabbed from the payload registration service. |
    +| base_tfrom_sensor | [bosdyn.api.SE3Pose](#bosdyn.api.SE3Pose) | The transform from the base of spot cam to this specific camera. |
    +| pinhole | [Camera.PinholeIntrinsics](#bosdyn.api.spot_cam.Camera.PinholeIntrinsics) | Physical cameras |
    +| spherical | [Camera.SphericalLimits](#bosdyn.api.spot_cam.Camera.SphericalLimits) | Only synthetic spherical panoramas |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Camera.PinholeIntrinsics
    +
    +Parameters for a pinhole model of distortion.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| focal_length | [bosdyn.api.Vec2](#bosdyn.api.Vec2) | Focal_length in pixels |
    +| center_point | [bosdyn.api.Vec2](#bosdyn.api.Vec2) | Center point in pixels |
    +| k1 | [float](#float) | The following 4 parameters are radial distortion coefficeints to 4 orders. See https://en.wikipedia.org/wiki/Distortion_(optics)#Software_correction If all 4 of these values are 0, do not apply any correction. |
    +| k2 | [float](#float) |  |
    +| k3 | [float](#float) |  |
    +| k4 | [float](#float) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Camera.SphericalLimits
    +
    +Spherical limits are the minimum and maximum angle of the image;
    +IE the upper left pixel is at min_angle.x, min_angle.y
    +and the lower right pixel is at max_angle.x, max_angle.y
    +for a full-FOV image this will be (-180, 90) and (180, -90)
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| min_angle | [bosdyn.api.Vec2](#bosdyn.api.Vec2) | Upper left pixel location in degrees. |
    +| max_angle | [bosdyn.api.Vec2](#bosdyn.api.Vec2) | Lower right pixel location in degrees. |
    +
    +
    +
    +
    +
    + 
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# spot_cam/compositor.proto
    +
    +
    +
    +
    +
    +### GetIrColormapRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetIrColormapResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) |  |
    +| map | [IrColorMap](#bosdyn.api.spot_cam.IrColorMap) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetScreenRequest
    +
    +Request the current screen in use.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetScreenResponse
    +
    +Specify which screen is currently being displayed in the video stream.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| name | [string](#string) | Identifier of the current screen. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetVisibleCamerasRequest
    +
    +Request information about the current cameras in the video stream.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetVisibleCamerasResponse
    +
    +Description of the parameters and locations of each camera in the
    +current video stream.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| streams | [GetVisibleCamerasResponse.Stream](#bosdyn.api.spot_cam.GetVisibleCamerasResponse.Stream) | List of all camera streams visible in the current video stream. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetVisibleCamerasResponse.Stream
    +
    +The location and camera parameters for a single camera.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| window | [GetVisibleCamerasResponse.Stream.Window](#bosdyn.api.spot_cam.GetVisibleCamerasResponse.Stream.Window) | The location of this camera stream within the larger stream. |
    +| camera | [Camera](#bosdyn.api.spot_cam.Camera) | The name field in this camera member is of the form 'c:w', where c is the name of the camera and w is the name of the window that's projecting it. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetVisibleCamerasResponse.Stream.Window
    +
    +The location of a sub-image within a larger image.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| xoffset | [int32](#int32) |  |
    +| yoffset | [int32](#int32) |  |
    +| width | [int32](#int32) | The image should be cropped out of the stream at this resolution, and then scaled to the resolution described in the 'camera' member, below. once that scaling takes place, the intrinsics will be valid. |
    +| height | [int32](#int32) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### IrColorMap
    +
    +the colormap is a mapping of radiometric data to color, to make the images easier for people to look at in real time.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| colormap | [IrColorMap.ColorMap](#bosdyn.api.spot_cam.IrColorMap.ColorMap) |  |
    +| scale | [IrColorMap.ScalingPair](#bosdyn.api.spot_cam.IrColorMap.ScalingPair) |  |
    +| auto_scale | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | if auto_scale is true, then the min and max values are derived from the data itself, and the settings above are ignored |
    +
    +
    +
    +
    +
    +
    +
    +
    +### IrColorMap.ScalingPair
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| min | [double](#double) | the minimum value to do color mapping, in degrees Celsius |
    +| max | [double](#double) | the maximum value to do color mapping, in degrees Celsius |
    +
    +
    +
    +
    +
    +
    +
    +
    +### IrMeterOverlay
    +
    +the ir meter overlay allows for pixel-accurate measurements to be taken and displayed to the user
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| enable | [bool](#bool) | If enable isn't true, don't overlay any IR meter |
    +| coords | [IrMeterOverlay.NormalizedCoordinates](#bosdyn.api.spot_cam.IrMeterOverlay.NormalizedCoordinates) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### IrMeterOverlay.NormalizedCoordinates
    +
    +these coordinates, normalized from 0-1, are within the ir camera 'window'
    +note: if the coordinates lie within an 'invalid' region of the window, then
    +the meter will be disabled.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| x | [double](#double) |  |
    +| y | [double](#double) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListScreensRequest
    +
    +Request the different screen layouts available.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListScreensResponse
    +
    +Response with all screen layouts available.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| screens | [ScreenDescription](#bosdyn.api.spot_cam.ScreenDescription) | List of all screen layouts that can be selected. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ScreenDescription
    +
    +A "Screen" represents a particular layout of camera images
    +used by the video stream.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| name | [string](#string) | Unique identifer for a screen. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetIrColormapRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) |  |
    +| map | [IrColorMap](#bosdyn.api.spot_cam.IrColorMap) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetIrColormapResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetIrMeterOverlayRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) |  |
    +| overlay | [IrMeterOverlay](#bosdyn.api.spot_cam.IrMeterOverlay) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetIrMeterOverlayResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetScreenRequest
    +
    +Switch the camera layout in the video stream to the one specified.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| name | [string](#string) | Identifier as specified in ListScreensResponse. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetScreenResponse
    +
    +Result of setting the camera layout.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| name | [string](#string) | Identifier of the screen used. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### IrColorMap.ColorMap
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| COLORMAP_UNKNOWN | 0 |  |
    +| COLORMAP_GREYSCALE | 1 | the greyscale colormap maps the minimum value (defined below) to black and the maximum value (defined below) to white |
    +| COLORMAP_JET | 2 | the jet colormap uses blues for values closer to the minimum, and red values for values closer to the maximum. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# spot_cam/health.proto
    +
    +
    +
    +
    +
    +### ClearBITEventsRequest
    +
    +Clear Built-in Test events.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ClearBITEventsResponse
    +
    +Response to clearing built-in test events.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetBITStatusRequest
    +
    +Request the status of all built-in tests.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetBITStatusResponse
    +
    +Data on the current status of built-in tests.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| events | [bosdyn.api.SystemFault](#bosdyn.api.SystemFault) | Fault events that have been reported. |
    +| degradations | [GetBITStatusResponse.Degradation](#bosdyn.api.spot_cam.GetBITStatusResponse.Degradation) | List of system states that may effect performance. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetBITStatusResponse.Degradation
    +
    +Degredations are not necessesarily faults; a unit
    +with no installed mechanical PTZ will behave differently,
    +but nothing's actually wrong.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| type | [GetBITStatusResponse.Degradation.DegradationType](#bosdyn.api.spot_cam.GetBITStatusResponse.Degradation.DegradationType) | System affected. |
    +| description | [string](#string) | Description of the kind of degradation being experienced. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetTemperatureRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetTemperatureResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| temps | [Temperature](#bosdyn.api.spot_cam.Temperature) | List of all temperatures measured. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Temperature
    +
    +The temperature of a particular component.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| channel_name | [string](#string) | Identifier of the hardware measured. |
    +| temperature | [int64](#int64) | Temperature is expressed in millidegrees C. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### GetBITStatusResponse.Degradation.DegradationType
    +
    +Systems that can experience performance degredations.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STORAGE | 0 |  |
    +| PTZ | 1 |  |
    +| LED | 2 |  |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# spot_cam/logging.proto
    +
    +
    +
    +
    +
    +### DebugRequest
    +
    +Change debug logging settings on the SpotCam.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| enable_temperature | [bool](#bool) | Set true to enable logging of temperature data; |
    +| enable_humidity | [bool](#bool) | Set true to enable logging of humidity data; |
    +| enable_BIT | [bool](#bool) | Set true to enable logging of BIT events; BIT events are always recorded to volatile memory and can be viewed (and cleared) with the Health service, but this enables writing them to disk. |
    +| enable_shock | [bool](#bool) | Set true to enable logging of Shock data; this is on by default. |
    +| enable_system_stat | [bool](#bool) | Set to true to enable logging of system load stats cpu, gpu, memory, and network utilization
    +
    +Nowow a BIT, set true to enable logging of led driver status. bool enable_led_stat = 7; |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DebugResponse
    +
    +Response with any errors for debug setting changes.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DeleteRequest
    +
    +Delete a log point from the store.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| point | [Logpoint](#bosdyn.api.spot_cam.Logpoint) | Log point to delete. Only the name is used. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DeleteResponse
    +
    +Response to a deletion with any errors that occurred.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetStatusRequest
    +
    +Request for status about the current stage of data acquisition.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| point | [Logpoint](#bosdyn.api.spot_cam.Logpoint) | Log point to query. Only the name is used. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetStatusResponse
    +
    +Provide an update on the stage of data acquisition.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| point | [Logpoint](#bosdyn.api.spot_cam.Logpoint) | The logpoint returned here can be used to add a tag to the Logpoint later |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListCamerasRequest
    +
    +Request the available cameras.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListCamerasResponse
    +
    +Provide the list of available cameras.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| cameras | [Camera](#bosdyn.api.spot_cam.Camera) | List of all cameras which can be used in a StoreRequest. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListLogpointsRequest
    +
    +List all available log points, whether they have completed or not.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListLogpointsResponse
    +
    +Provide all log points in the system.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| logpoints | [Logpoint](#bosdyn.api.spot_cam.Logpoint) | List of all the individual log points concatenated into a list. This stream may take a long time to complete if there are a lot of stored images. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Logpoint
    +
    +A representation of a stored data acquisition.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| name | [string](#string) | Unique identifier for a data acquisition event. |
    +| type | [Logpoint.RecordType](#bosdyn.api.spot_cam.Logpoint.RecordType) | Type of data held in this log point. |
    +| status | [Logpoint.LogStatus](#bosdyn.api.spot_cam.Logpoint.LogStatus) | Current stage of acquisition. |
    +| queue_status | [Logpoint.QueueStatus](#bosdyn.api.spot_cam.Logpoint.QueueStatus) | Only filled out when status == QUEUED |
    +| tag | [string](#string) | An arbitrary string to be stored with the log data. |
    +| timestamp | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Time of acquisition. |
    +| image_params | [Logpoint.ImageParams](#bosdyn.api.spot_cam.Logpoint.ImageParams) | Image format of the stored data. |
    +| calibration | [Logpoint.Calibration](#bosdyn.api.spot_cam.Logpoint.Calibration) | Camera data for all sub-images contained within the image data. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Logpoint.Calibration
    +
    +Data describing the camera intrinsics and extrinsics for a window of the image.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| xoffset | [int32](#int32) |  |
    +| yoffset | [int32](#int32) |  |
    +| width | [int32](#int32) |  |
    +| height | [int32](#int32) |  |
    +| base_frame_name | [string](#string) |  |
    +| base_tfrom_sensor | [bosdyn.api.SE3Pose](#bosdyn.api.SE3Pose) |  |
    +| intrinsics | [Camera.PinholeIntrinsics](#bosdyn.api.spot_cam.Camera.PinholeIntrinsics) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Logpoint.ImageParams
    +
    +Description of image format.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| width | [int32](#int32) |  |
    +| height | [int32](#int32) |  |
    +| format | [bosdyn.api.Image.PixelFormat](#bosdyn.api.Image.PixelFormat) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RetrieveRawDataRequest
    +
    +Retrieve the binary data associated with a log point, with no processing applied.
    +Storing a panorama will retrieve tiled individual images.
    +For IR, the temperature at each pixel is 0.1 * the int value in Kelvin.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| point | [Logpoint](#bosdyn.api.spot_cam.Logpoint) | Log point to retrieve. Only the name is used. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RetrieveRawDataResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| logpoint | [Logpoint](#bosdyn.api.spot_cam.Logpoint) | Log point retrieved. |
    +| data | [bosdyn.api.DataChunk](#bosdyn.api.DataChunk) | Data chunk bytes field should be concatenated together to recover the binary data. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RetrieveRequest
    +
    +Retrieve the binary data associated with a log point.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| point | [Logpoint](#bosdyn.api.spot_cam.Logpoint) | Log point to retrieve. Only the name is used. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RetrieveResponse
    +
    +Provide the data stored at a log point.
    +Store() dictates what processing happens in this response.
    +c0 -> c4 will return the raw (rgb24) fisheye image of the camera at that index.
    +Storing a panorama will process the data into a stitched image.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| logpoint | [Logpoint](#bosdyn.api.spot_cam.Logpoint) | Log point retrieved. |
    +| data | [bosdyn.api.DataChunk](#bosdyn.api.DataChunk) | Data chunk bytes field should be concatenated together to recover the binary data. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetPassphraseRequest
    +
    +Set encryption for the disk.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| passphrase | [string](#string) | After setting the passphrase, please reboot the system to remount the encrypted filesystem layer. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetPassphraseResponse
    +
    +Response from setting the disk encryption.
    +After setting the passphrase, please reboot the system to
    +remount the encrypted filesystem layer.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StoreRequest
    +
    +Trigger a data acquisition.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| camera | [Camera](#bosdyn.api.spot_cam.Camera) | Which camera to capture. |
    +| type | [Logpoint.RecordType](#bosdyn.api.spot_cam.Logpoint.RecordType) | Type of data capture to perform. |
    +| tag | [string](#string) | Metadata to associate with the store. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StoreResponse
    +
    +Result of data acquisition trigger.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| point | [Logpoint](#bosdyn.api.spot_cam.Logpoint) | The log point returned here can be used to add a tag to the Logpoint later It will very likely be in th 'QUEUED' state. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### TagRequest
    +
    +Add tag metadata to an existing log point.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| point | [Logpoint](#bosdyn.api.spot_cam.Logpoint) | Logpoint to add metadata to. Name and tag are used. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### TagResponse
    +
    +Result of adding tag metadata to a log point.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### Logpoint.LogStatus
    +
    +Possible stages of data acquisition.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| FAILED | 0 |  |
    +| QUEUED | 1 | the logpoint has been queued to be downloaded from the renderer |
    +| COMPLETE | 2 | the logpoint is written to the disk |
    +| UNKNOWN | -1 |  |
    +
    +
    +
    +
    +
    +### Logpoint.QueueStatus
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| QUEUED_UNKNOWN | 0 |  |
    +| QUEUED_RENDER | 1 | The logpoint has been queued to be downloaded from the renderer |
    +| QUEUED_DISK | 2 | The logpoint is in general ram, and will be written to the disk when resources allow |
    +
    +
    +
    +
    +
    +### Logpoint.RecordType
    +
    +Possible types of media that can be stored.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STILLIMAGE | 0 |  |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# spot_cam/network.proto
    +
    +
    +
    +
    +
    +### GetICEConfigurationRequest
    +
    +Request the servers used for ICE resolution.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetICEConfigurationResponse
    +
    +Provides the ICE resolution servers.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| servers | [ICEServer](#bosdyn.api.spot_cam.ICEServer) | List of servers used for ICE resolution. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetNetworkSettingsRequest
    +
    +Retrieve current network configuration.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetNetworkSettingsResponse
    +
    +Provides the current network configuration.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| settings | [NetworkTuple](#bosdyn.api.spot_cam.NetworkTuple) | Current network configuration. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetSSLCertRequest
    +
    +Request the SSL certificate currently in use.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetSSLCertResponse
    +
    +Provides the SSL certificate currently in use.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| certificate | [string](#string) | An ASCII-armored representation of the SSL certificate |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ICEServer
    +
    +Servers used in the ICE resolution process.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| type | [ICEServer.servertype](#bosdyn.api.spot_cam.ICEServer.servertype) | STUN or TURN server. |
    +| address | [string](#string) | Network address of the server. |
    +| port | [uint32](#uint32) | Only the least significant 16 bits are used. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### NetworkTuple
    +
    +Network configuration data.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| address | [google.protobuf.UInt32Value](#google.protobuf.UInt32Value) | a big-endian representation of an IPv4 address |
    +| netmask | [google.protobuf.UInt32Value](#google.protobuf.UInt32Value) | The mask used for defining the system's subnet |
    +| gateway | [google.protobuf.UInt32Value](#google.protobuf.UInt32Value) | A global routing is set up for the address defined below (if present) |
    +| mtu | [google.protobuf.UInt32Value](#google.protobuf.UInt32Value) | If MTU is present, and <16 bits wide, then it is set for the ethernet interface's MTU if not, the MTU is set to 1500 |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetICEConfigurationRequest
    +
    +Modify the ICE configuration.
    +Note: this configuration replaces any configuration currently present.
    +It is *not* appended.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| servers | [ICEServer](#bosdyn.api.spot_cam.ICEServer) | List of servers used for ICE resolution. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetICEConfigurationResponse
    +
    +Result of modifying the ICE configuration.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### ICEServer.servertype
    +
    +Possible types of servers
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| UNKNOWN | 0 |  |
    +| STUN | 1 |  |
    +| TURN | 2 |  |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# spot_cam/power.proto
    +
    +
    +
    +
    +
    +### CyclePowerRequest
    +
    +Turn components off and then back on without needing two separate requests.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| status | [PowerStatus](#bosdyn.api.spot_cam.PowerStatus) | status indicates the devices for which cycle-power is requested 'true' for cycle-power, else no effect power cycle will not be performed on a given device if its state is power-off prior to this call |
    +
    +
    +
    +
    +
    +
    +
    +
    +### CyclePowerResponse
    +
    +Result of power cycling components.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [PowerStatus](#bosdyn.api.spot_cam.PowerStatus) | status indicates the power status of the controllable devices after a successful power cycle 'true' for power-on, 'false' for power-off |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetPowerStatusRequest
    +
    +Request component power status.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetPowerStatusResponse
    +
    +Provides the power status of all components.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [PowerStatus](#bosdyn.api.spot_cam.PowerStatus) | status indicates the power status of the controllable devices 'true' for power-on, 'false' for power-off |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PowerStatus
    +
    +Power on or off of components of the SpotCam.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| ptz | [google.protobuf.BoolValue](#google.protobuf.BoolValue) | these switches are 'true' for power-on, 'false' for power-off |
    +| aux1 | [google.protobuf.BoolValue](#google.protobuf.BoolValue) |  |
    +| aux2 | [google.protobuf.BoolValue](#google.protobuf.BoolValue) |  |
    +| external_mic | [google.protobuf.BoolValue](#google.protobuf.BoolValue) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetPowerStatusRequest
    +
    +Turn components on or off.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| status | [PowerStatus](#bosdyn.api.spot_cam.PowerStatus) | status indicates the requested power status of the controllable devices 'true' for power-on, 'false' for power-off |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetPowerStatusResponse
    +
    +Result of turning components on or off.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| status | [PowerStatus](#bosdyn.api.spot_cam.PowerStatus) | status indicates the requested changes upon success 'true' for power-on, 'false' for power-off |
    +
    +
    +
    +
    +
    + 
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# spot_cam/ptz.proto
    +
    +
    +
    +
    +
    +### GetPtzPositionRequest
    +
    +Request the current position of a ptz.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| ptz | [PtzDescription](#bosdyn.api.spot_cam.PtzDescription) | Only the name is used. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetPtzPositionResponse
    +
    +Provides the current measured position.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| position | [PtzPosition](#bosdyn.api.spot_cam.PtzPosition) | Current position of the mechanism. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetPtzVelocityRequest
    +
    +Request the velocity of a ptz
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| ptz | [PtzDescription](#bosdyn.api.spot_cam.PtzDescription) | Only the name is used. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetPtzVelocityResponse
    +
    +Provides the current measured velocity.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| velocity | [PtzVelocity](#bosdyn.api.spot_cam.PtzVelocity) | Current velocity of the mechanism. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### InitializeLensRequest
    +
    +Command to reset PTZ autofocus
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common response header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### InitializeLensResponse
    +
    +Result of a InitializeLensRequest.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListPtzRequest
    +
    +Request all available ptzs on the SpotCam.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListPtzResponse
    +
    +Provide all available ptz on the SpotCam.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| ptzs | [PtzDescription](#bosdyn.api.spot_cam.PtzDescription) | List of ptzs, real and virtual. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PtzDescription
    +
    +PtzDescription provides information about a given PTZ. The name is usually all that's required to
    +describe a PTZ, but ListPtzResponse will include more information.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| name | [string](#string) | Identifier of a particular controllable PTZ mechanism (real or virtual). |
    +| pan_limit | [PtzDescription.Limits](#bosdyn.api.spot_cam.PtzDescription.Limits) | If a limit is not set, all positions are valid
    +
    +Limits in degrees. |
    +| tilt_limit | [PtzDescription.Limits](#bosdyn.api.spot_cam.PtzDescription.Limits) | Limits in degrees. |
    +| zoom_limit | [PtzDescription.Limits](#bosdyn.api.spot_cam.PtzDescription.Limits) | Limits in zoom level. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PtzDescription.Limits
    +
    +Limits for a single axis.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| min | [google.protobuf.FloatValue](#google.protobuf.FloatValue) | Units depend on the axis being controlled. |
    +| max | [google.protobuf.FloatValue](#google.protobuf.FloatValue) | Units depend on the axis being controlled. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PtzPosition
    +
    +Doubles as a description of current state, or a command for a new position.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| ptz | [PtzDescription](#bosdyn.api.spot_cam.PtzDescription) | The "mech" ptz can pan [0, 360] degrees, tilt [-20, 217] degrees where 0 is the horizon, and zoom between 1x and 30x. |
    +| pan | [google.protobuf.FloatValue](#google.protobuf.FloatValue) | degrees |
    +| tilt | [google.protobuf.FloatValue](#google.protobuf.FloatValue) | degrees |
    +| zoom | [google.protobuf.FloatValue](#google.protobuf.FloatValue) | zoom level |
    +
    +
    +
    +
    +
    +
    +
    +
    +### PtzVelocity
    +
    +Doubles as a description of current state, or a command for a new velocity.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| ptz | [PtzDescription](#bosdyn.api.spot_cam.PtzDescription) | The "mech" ptz cannot be used with Velocity. |
    +| pan | [google.protobuf.FloatValue](#google.protobuf.FloatValue) | degrees/second |
    +| tilt | [google.protobuf.FloatValue](#google.protobuf.FloatValue) | degrees/second |
    +| zoom | [google.protobuf.FloatValue](#google.protobuf.FloatValue) | zoom level/second |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetPtzPositionRequest
    +
    +Command the ptz to move to a position.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| position | [PtzPosition](#bosdyn.api.spot_cam.PtzPosition) | Desired position to achieve. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetPtzPositionResponse
    +
    +Result of a SetPtzPositionRequest.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| position | [PtzPosition](#bosdyn.api.spot_cam.PtzPosition) | Applied desired position. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetPtzVelocityRequest
    +
    +Command a velocity for a ptz.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| velocity | [PtzVelocity](#bosdyn.api.spot_cam.PtzVelocity) | Desired velocity to achieve. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetPtzVelocityResponse
    +
    +Result of a SetPtzVelocityRequest.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| velocity | [PtzVelocity](#bosdyn.api.spot_cam.PtzVelocity) | Applied desired position. |
    +
    +
    +
    +
    +
    + 
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# spot_cam/service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### AudioService
    +
    +Upload and play sounds over the SpotCam's speakers.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| PlaySound | [PlaySoundRequest](#bosdyn.api.spot_cam.PlaySoundRequest) | [PlaySoundResponse](#bosdyn.api.spot_cam.PlaySoundResponse) | Given a soundRequest that identifies a single sound present in the system's sound effects table, PlaySound executes the sound effect. |
    +| LoadSound | [LoadSoundRequest](#bosdyn.api.spot_cam.LoadSoundRequest) stream | [LoadSoundResponse](#bosdyn.api.spot_cam.LoadSoundResponse) | LoadSound loads a sound effect into the system's sound table. The stream must contain a wav file, with a RIFF header describing it. The arguement is a stream, to allow for sounds that are bigger then the MTU of the network; in this case, the complete stream must contain the entire sound. If the stream ends early, an error will be returned. The header and sound fields of the entire stream must be the same. |
    +| DeleteSound | [DeleteSoundRequest](#bosdyn.api.spot_cam.DeleteSoundRequest) | [DeleteSoundResponse](#bosdyn.api.spot_cam.DeleteSoundResponse) | Delete the sound identified in the argument from the system's sound table. |
    +| ListSounds | [ListSoundsRequest](#bosdyn.api.spot_cam.ListSoundsRequest) | [ListSoundsResponse](#bosdyn.api.spot_cam.ListSoundsResponse) | ListSounds returns a list of all of the sound effects that the system knows about. |
    +| SetVolume | [SetVolumeRequest](#bosdyn.api.spot_cam.SetVolumeRequest) | [SetVolumeResponse](#bosdyn.api.spot_cam.SetVolumeResponse) | Set the overall volume level for playing sounds. |
    +| GetVolume | [GetVolumeRequest](#bosdyn.api.spot_cam.GetVolumeRequest) | [GetVolumeResponse](#bosdyn.api.spot_cam.GetVolumeResponse) | Set the overall volume level for playing sounds. |
    +| SetAudioCaptureChannel | [SetAudioCaptureChannelRequest](#bosdyn.api.spot_cam.SetAudioCaptureChannelRequest) | [SetAudioCaptureChannelResponse](#bosdyn.api.spot_cam.SetAudioCaptureChannelResponse) |  |
    +| GetAudioCaptureChannel | [GetAudioCaptureChannelRequest](#bosdyn.api.spot_cam.GetAudioCaptureChannelRequest) | [GetAudioCaptureChannelResponse](#bosdyn.api.spot_cam.GetAudioCaptureChannelResponse) |  |
    +| SetAudioCaptureGain | [SetAudioCaptureGainRequest](#bosdyn.api.spot_cam.SetAudioCaptureGainRequest) | [SetAudioCaptureGainResponse](#bosdyn.api.spot_cam.SetAudioCaptureGainResponse) |  |
    +| GetAudioCaptureGain | [GetAudioCaptureGainRequest](#bosdyn.api.spot_cam.GetAudioCaptureGainRequest) | [GetAudioCaptureGainResponse](#bosdyn.api.spot_cam.GetAudioCaptureGainResponse) |  |
    +
    +
    +
    +
    +### CompositorService
    +
    +Change the layout of of the video stream between available presets.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| SetScreen | [SetScreenRequest](#bosdyn.api.spot_cam.SetScreenRequest) | [SetScreenResponse](#bosdyn.api.spot_cam.SetScreenResponse) | SetScreen changes the current view that is streamed over the network |
    +| GetScreen | [GetScreenRequest](#bosdyn.api.spot_cam.GetScreenRequest) | [GetScreenResponse](#bosdyn.api.spot_cam.GetScreenResponse) | GetScreen returns the currently-selected screen |
    +| ListScreens | [ListScreensRequest](#bosdyn.api.spot_cam.ListScreensRequest) | [ListScreensResponse](#bosdyn.api.spot_cam.ListScreensResponse) | ListScreens returns a list of available screens |
    +| GetVisibleCameras | [GetVisibleCamerasRequest](#bosdyn.api.spot_cam.GetVisibleCamerasRequest) | [GetVisibleCamerasResponse](#bosdyn.api.spot_cam.GetVisibleCamerasResponse) | GetVisibleCameras returns a list of currently visible windows, with any available metadata |
    +| SetIrColormap | [SetIrColormapRequest](#bosdyn.api.spot_cam.SetIrColormapRequest) | [SetIrColormapResponse](#bosdyn.api.spot_cam.SetIrColormapResponse) | set the mapping between radiometric IR samples to color, for video |
    +| GetIrColormap | [GetIrColormapRequest](#bosdyn.api.spot_cam.GetIrColormapRequest) | [GetIrColormapResponse](#bosdyn.api.spot_cam.GetIrColormapResponse) | get the mapping between radiometric IR samples to color, for video |
    +| SetIrMeterOverlay | [SetIrMeterOverlayRequest](#bosdyn.api.spot_cam.SetIrMeterOverlayRequest) | [SetIrMeterOverlayResponse](#bosdyn.api.spot_cam.SetIrMeterOverlayResponse) | apply settings for the 'ir meter overlay' |
    +
    +
    +
    +
    +### HealthService
    +
    +Query temperature and built-in test results.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| GetTemperature | [GetTemperatureRequest](#bosdyn.api.spot_cam.GetTemperatureRequest) | [GetTemperatureResponse](#bosdyn.api.spot_cam.GetTemperatureResponse) | GetTemperature returns a list of thermometers in the system, and the temperature that they measure. |
    +| GetBITStatus | [GetBITStatusRequest](#bosdyn.api.spot_cam.GetBITStatusRequest) | [GetBITStatusResponse](#bosdyn.api.spot_cam.GetBITStatusResponse) | GetBitStatus returns two lists; a list of system events, and a list of ways that the system is degraded; for instance, a degredation may include a missing PTZ unit, or a missing USB storage device. |
    +| ClearBITEvents | [ClearBITEventsRequest](#bosdyn.api.spot_cam.ClearBITEventsRequest) | [ClearBITEventsResponse](#bosdyn.api.spot_cam.ClearBITEventsResponse) | ClearBitEvents clears out the events list of the BITStatus structure. |
    +
    +
    +
    +
    +### LightingService
    +
    +Change the brightness level of individual LEDs.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| SetLEDBrightness | [SetLEDBrightnessRequest](#bosdyn.api.spot_cam.SetLEDBrightnessRequest) | [SetLEDBrightnessResponse](#bosdyn.api.spot_cam.SetLEDBrightnessResponse) |  |
    +| GetLEDBrightness | [GetLEDBrightnessRequest](#bosdyn.api.spot_cam.GetLEDBrightnessRequest) | [GetLEDBrightnessResponse](#bosdyn.api.spot_cam.GetLEDBrightnessResponse) |  |
    +
    +
    +
    +
    +### MediaLogService
    +
    +Trigger data acquisitions, and retrieve resulting data.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| Store | [StoreRequest](#bosdyn.api.spot_cam.StoreRequest) | [StoreResponse](#bosdyn.api.spot_cam.StoreResponse) | Store queues up a Logpoint, which is a bit of media that the user wishes to store to disk (still images are supported for now, more media types will be supported in the future) |
    +| GetStatus | [GetStatusRequest](#bosdyn.api.spot_cam.GetStatusRequest) | [GetStatusResponse](#bosdyn.api.spot_cam.GetStatusResponse) | GetStatus reads the 'name' field of the Logpoint contained in GetStatusRequest, and fills in the rest of the fields. Mainly useful for getting the 'state' of the logpoint. |
    +| Tag | [TagRequest](#bosdyn.api.spot_cam.TagRequest) | [TagResponse](#bosdyn.api.spot_cam.TagResponse) | Tag updates the 'tag' field of the Logpoint that's passed, which must exist. |
    +| EnableDebug | [DebugRequest](#bosdyn.api.spot_cam.DebugRequest) | [DebugResponse](#bosdyn.api.spot_cam.DebugResponse) | EnableDebug starts the periodic logging of health data to the database; this increases disk utilization, but will record data that is useful post-mortum |
    +| ListCameras | [ListCamerasRequest](#bosdyn.api.spot_cam.ListCamerasRequest) | [ListCamerasResponse](#bosdyn.api.spot_cam.ListCamerasResponse) | ListCameras returns a list of strings that identify valid cameras for logging |
    +| RetrieveRawData | [RetrieveRawDataRequest](#bosdyn.api.spot_cam.RetrieveRawDataRequest) | [RetrieveRawDataResponse](#bosdyn.api.spot_cam.RetrieveRawDataResponse) stream | Retrieve returns all raw data associated with a given logpoint |
    +| Retrieve | [RetrieveRequest](#bosdyn.api.spot_cam.RetrieveRequest) | [RetrieveResponse](#bosdyn.api.spot_cam.RetrieveResponse) stream | Retrieve returns all data associated with a given logpoint |
    +| Delete | [DeleteRequest](#bosdyn.api.spot_cam.DeleteRequest) | [DeleteResponse](#bosdyn.api.spot_cam.DeleteResponse) | Delete removes a Logpoint from the system |
    +| ListLogpoints | [ListLogpointsRequest](#bosdyn.api.spot_cam.ListLogpointsRequest) | [ListLogpointsResponse](#bosdyn.api.spot_cam.ListLogpointsResponse) stream | ListLogpoints returns a list of all logpoints in the database. Warning: this may be a lot of data. |
    +| SetPassphrase | [SetPassphraseRequest](#bosdyn.api.spot_cam.SetPassphraseRequest) | [SetPassphraseResponse](#bosdyn.api.spot_cam.SetPassphraseResponse) | SetPassphrase sets the eCryptFS passphrase used by the filesystem. there is no symmetry here, because key material is write-only This rpc is now deprecated as of the switch from EXT4 to NTFS and returns UnimplementedError |
    +
    +
    +
    +
    +### NetworkService
    +
    +Modify or query network settings of the SpotCam and ICE resolution servers.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| SetICEConfiguration | [SetICEConfigurationRequest](#bosdyn.api.spot_cam.SetICEConfigurationRequest) | [SetICEConfigurationResponse](#bosdyn.api.spot_cam.SetICEConfigurationResponse) | SetICEConfiguration sets up parameters for ICE, including addresses for STUN and TURN services |
    +| GetICEConfiguration | [GetICEConfigurationRequest](#bosdyn.api.spot_cam.GetICEConfigurationRequest) | [GetICEConfigurationResponse](#bosdyn.api.spot_cam.GetICEConfigurationResponse) | GetICEConfiguration retrieves currently set parameters for ICE, including addresses for STUN and TURN services |
    +
    +
    +
    +
    +### PowerService
    +
    +Turn hardware components' power on or off.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| SetPowerStatus | [SetPowerStatusRequest](#bosdyn.api.spot_cam.SetPowerStatusRequest) | [SetPowerStatusResponse](#bosdyn.api.spot_cam.SetPowerStatusResponse) | Turn components' power on or off. This should not be used to power cycle a component Turning PTZ power off for too long will cause the video stream to fail |
    +| GetPowerStatus | [GetPowerStatusRequest](#bosdyn.api.spot_cam.GetPowerStatusRequest) | [GetPowerStatusResponse](#bosdyn.api.spot_cam.GetPowerStatusResponse) | Get current status of a component |
    +| CyclePower | [CyclePowerRequest](#bosdyn.api.spot_cam.CyclePowerRequest) | [CyclePowerResponse](#bosdyn.api.spot_cam.CyclePowerResponse) | Cycle power for a component |
    +
    +
    +
    +
    +### PtzService
    +
    +Control real and virtual ptz mechanisms.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| SetPtzPosition | [SetPtzPositionRequest](#bosdyn.api.spot_cam.SetPtzPositionRequest) | [SetPtzPositionResponse](#bosdyn.api.spot_cam.SetPtzPositionResponse) | SetPosition points the referenced camera to a given vector (in PTZ-space) |
    +| GetPtzPosition | [GetPtzPositionRequest](#bosdyn.api.spot_cam.GetPtzPositionRequest) | [GetPtzPositionResponse](#bosdyn.api.spot_cam.GetPtzPositionResponse) | GetPosition returns the current settings of the referenced camera |
    +| SetPtzVelocity | [SetPtzVelocityRequest](#bosdyn.api.spot_cam.SetPtzVelocityRequest) | [SetPtzVelocityResponse](#bosdyn.api.spot_cam.SetPtzVelocityResponse) |  |
    +| GetPtzVelocity | [GetPtzVelocityRequest](#bosdyn.api.spot_cam.GetPtzVelocityRequest) | [GetPtzVelocityResponse](#bosdyn.api.spot_cam.GetPtzVelocityResponse) |  |
    +| ListPtz | [ListPtzRequest](#bosdyn.api.spot_cam.ListPtzRequest) | [ListPtzResponse](#bosdyn.api.spot_cam.ListPtzResponse) |  |
    +| InitializeLens | [InitializeLensRequest](#bosdyn.api.spot_cam.InitializeLensRequest) | [InitializeLensResponse](#bosdyn.api.spot_cam.InitializeLensResponse) | Reinitializes PTZ autofocus |
    +
    +
    +
    +
    +### StreamQualityService
    +
    +Set quality parameters for the stream, such as compression and image postprocessing settings.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| SetStreamParams | [SetStreamParamsRequest](#bosdyn.api.spot_cam.SetStreamParamsRequest) | [SetStreamParamsResponse](#bosdyn.api.spot_cam.SetStreamParamsResponse) |  |
    +| GetStreamParams | [GetStreamParamsRequest](#bosdyn.api.spot_cam.GetStreamParamsRequest) | [GetStreamParamsResponse](#bosdyn.api.spot_cam.GetStreamParamsResponse) |  |
    +| EnableCongestionControl | [EnableCongestionControlRequest](#bosdyn.api.spot_cam.EnableCongestionControlRequest) | [EnableCongestionControlResponse](#bosdyn.api.spot_cam.EnableCongestionControlResponse) |  |
    +
    +
    +
    +
    +### VersionService
    +
    +Query the version of the software release running on the SpotCam.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| GetSoftwareVersion | [GetSoftwareVersionRequest](#bosdyn.api.spot_cam.GetSoftwareVersionRequest) | [GetSoftwareVersionResponse](#bosdyn.api.spot_cam.GetSoftwareVersionResponse) |  |
    +
    + 
    +
    +
    +
    +
    +
    +# spot_cam/streamquality.proto
    +
    +
    +
    +
    +
    +### EnableCongestionControlRequest
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) |  |
    +| enable_congestion_control | [bool](#bool) | A boolean 'true' enables receiver congestion control while 'false' disables it |
    +
    +
    +
    +
    +
    +
    +
    +
    +### EnableCongestionControlResponse
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetStreamParamsRequest
    +
    +Request the current video stream parameters.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetStreamParamsResponse
    +
    +Provides the current video stream parameters.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| params | [StreamParams](#bosdyn.api.spot_cam.StreamParams) | Current video stream parameters. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetStreamParamsRequest
    +
    +Modify the video stream parameters.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| params | [StreamParams](#bosdyn.api.spot_cam.StreamParams) | Set only the fields that should be modified. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SetStreamParamsResponse
    +
    +Result of setting video stream parameters.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| params | [StreamParams](#bosdyn.api.spot_cam.StreamParams) | Applied video stream parameters. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StreamParams
    +
    +Parameters for how the video stream should be processed and compressed.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| targetbitrate | [google.protobuf.Int64Value](#google.protobuf.Int64Value) | The compression level in target BPS |
    +| refreshinterval | [google.protobuf.Int64Value](#google.protobuf.Int64Value) | How often should the entire feed be refreshed? (in frames) Note: the feed is refreshed on a macroblock level; there are no full I-frames |
    +| idrinterval | [google.protobuf.Int64Value](#google.protobuf.Int64Value) | How often should an IDR message get sent? (in frames) |
    +| awb | [StreamParams.AwbMode](#bosdyn.api.spot_cam.StreamParams.AwbMode) | Optional setting of automatic white balancing mode. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StreamParams.AwbMode
    +
    +Wrapper for AwbModeEnum to allow it to be optionally set.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| awb | [StreamParams.AwbModeEnum](#bosdyn.api.spot_cam.StreamParams.AwbModeEnum) |  |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### StreamParams.AwbModeEnum
    +
    +Options for automatic white balancing mode.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| OFF | 0 |  |
    +| AUTO | 1 |  |
    +| INCANDESCENT | 2 |  |
    +| FLUORESCENT | 3 |  |
    +| WARM_FLUORESCENT | 4 |  |
    +| DAYLIGHT | 5 |  |
    +| CLOUDY | 6 |  |
    +| TWILIGHT | 7 |  |
    +| SHADE | 8 |  |
    +| DARK | 9 |  |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# spot_cam/version.proto
    +
    +
    +
    +
    +
    +### GetSoftwareVersionRequest
    +
    +Request the software version running on the SpotCam.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### GetSoftwareVersionResponse
    +
    +Provide the SpotCam's software release version.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [bosdyn.api.ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| version | [bosdyn.api.SoftwareVersion](#bosdyn.api.SoftwareVersion) | Version of the software currently running on the SpotCam. |
    +| detail | [string](#string) | Extra detail about the version of software running on spotcam. May contain metadata about build dates and configuration. |
    +
    +
    +
    +
    +
    + 
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# stairs.proto
    +
    +
    +
    +
    +
    +### StairTransform
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| frame_tform_stairs | [SE3Pose](#bosdyn.api.SE3Pose) | The staircase origin is the bottom-center of the first rise. |
    +| frame_name | [string](#string) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StraightStaircase
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| from_ko_tform_stairs | [SE3Pose](#bosdyn.api.SE3Pose) | It is expressed in ko frame of the from_waypoint. This field is only used in GraphNav. |
    +| tform | [StairTransform](#bosdyn.api.StairTransform) | Outside GraphNav, this field specifies the stair origin. |
    +| stairs | [StraightStaircase.Stair](#bosdyn.api.StraightStaircase.Stair) | Each stair should be rise followed by run. The last stair will have zero run. |
    +| bottom_landing | [StraightStaircase.Landing](#bosdyn.api.StraightStaircase.Landing) | The lowermost landing of the stairs. The robot will try to align itself to the stairs while on this landing. |
    +| top_landing | [StraightStaircase.Landing](#bosdyn.api.StraightStaircase.Landing) | The uppermost landing of the stairs. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StraightStaircase.Landing
    +
    +Straight staircases have two landings, one at the top and one at the bottom.
    +Landings are areas of free space before and after the stairs, and are represented
    +as oriented bounding boxes.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| stairs_tform_landing_center | [SE3Pose](#bosdyn.api.SE3Pose) | Pose of the landing's center relative to the stairs frame. |
    +| landing_extent_x | [double](#double) | The half-size of the box representing the landing in the x axis. |
    +| landing_extent_y | [double](#double) | The half-size of the box representing the landing in the y axis. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### StraightStaircase.Stair
    +
    +A single stair from a staircase.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| rise | [float](#float) | Height of each stair. |
    +| run | [float](#float) | Depth of each stair. |
    +
    +
    +
    +
    +
    + 
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# synchronized_command.proto
    +
    +
    +
    +
    +
    +### SynchronizedCommand
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +### SynchronizedCommand.Feedback
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| arm_command_feedback | [ArmCommand.Feedback](#bosdyn.api.ArmCommand.Feedback) |  |
    +| mobility_command_feedback | [MobilityCommand.Feedback](#bosdyn.api.MobilityCommand.Feedback) |  |
    +| gripper_command_feedback | [GripperCommand.Feedback](#bosdyn.api.GripperCommand.Feedback) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SynchronizedCommand.Request
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| arm_command | [ArmCommand.Request](#bosdyn.api.ArmCommand.Request) |  |
    +| mobility_command | [MobilityCommand.Request](#bosdyn.api.MobilityCommand.Request) |  |
    +| gripper_command | [GripperCommand.Request](#bosdyn.api.GripperCommand.Request) |  |
    +
    +
    +
    +
    +
    + 
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# time_range.proto
    +
    +
    +
    +
    +
    +### TimeRange
    +
    +Representation of a time range from a start time through an end time.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| start | [google.protobuf.Timestamp](#google.protobuf.Timestamp) |  |
    +| end | [google.protobuf.Timestamp](#google.protobuf.Timestamp) |  |
    +
    +
    +
    +
    +
    + 
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# time_sync.proto
    +
    +
    +
    +
    +
    +### TimeSyncEstimate
    +
    +Estimate of network speed and clock skew.  Both for the last
    +complete sample and a recent average.  Populated by the server.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| round_trip_time | [google.protobuf.Duration](#google.protobuf.Duration) | Observed network delay (excludes processing between server_rx and server_tx). If zero, this estimate is unpopulated. |
    +| clock_skew | [google.protobuf.Duration](#google.protobuf.Duration) | Add the skew to the client system clock to get the server clock. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### TimeSyncRoundTrip
    +
    +Timestamp information from a full GRPC call round-trip.
    +These are used to estimate the round-trip communication time and difference between
    +client and server clocks.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| client_tx | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Client system time when the message was sent, if not zero. |
    +| server_rx | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Server system time when the message was received, if not zero. |
    +| server_tx | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Server system time when the response was sent, if not zero. |
    +| client_rx | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Client time when the response was received, if not zero. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### TimeSyncState
    +
    +Current best estimate status of time sync.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| best_estimate | [TimeSyncEstimate](#bosdyn.api.TimeSyncEstimate) | Best clock synchronization estimate currently available, if any. |
    +| status | [TimeSyncState.Status](#bosdyn.api.TimeSyncState.Status) | STATUS_OK once time sync is established. |
    +| measurement_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Time of best estimate, in server time. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### TimeSyncUpdateRequest
    +
    +Request message for a time-sync Update RPC.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header. |
    +| previous_round_trip | [TimeSyncRoundTrip](#bosdyn.api.TimeSyncRoundTrip) | Round-trip timing information from the previous Update request. |
    +| clock_identifier | [string](#string) | Identifier to verify time sync between robot and client. If unset, server will assign one to client. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### TimeSyncUpdateResponse
    +
    +Request message for a time-sync Update RPC.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header. |
    +| previous_estimate | [TimeSyncEstimate](#bosdyn.api.TimeSyncEstimate) | Clock synchronization estimate from the previous RPC round-trip, if available. |
    +| state | [TimeSyncState](#bosdyn.api.TimeSyncState) | Current best clock synchronization estimate according to server. |
    +| clock_identifier | [string](#string) | Identifier to verify time sync between robot and client. Assigned upon first Request and echoed with each subsequent request. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### TimeSyncState.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Invalid, do not use. |
    +| STATUS_OK | 1 | Clock skew is available. |
    +| STATUS_MORE_SAMPLES_NEEDED | 2 | More updates are required to establish a synchronization estimate. |
    +| STATUS_SERVICE_NOT_READY | 3 | Server still establishing time sync internally. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# time_sync_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### TimeSyncService
    +
    +The time-sync service estimates the difference between server and client clocks.
    +Time synchronization is a tool which allows applications to work in a unified timebase with
    +precision. It is useful in cases where a precise time must be set, independently of network
    +communication lag. In distributed systems and robotics, hardware, system-level, and per-process
    +approaches can be used to obtain synchronization.
    +This service implements a stand alone time synchronization service. It enables clients to
    +establish a per-process offset between two processes which may be on separate systems.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| TimeSyncUpdate | [TimeSyncUpdateRequest](#bosdyn.api.TimeSyncUpdateRequest) | [TimeSyncUpdateResponse](#bosdyn.api.TimeSyncUpdateResponse) | See the exchange documentation in time_sync.proto. This call makes one client/server round trip toward clock synchronization. |
    +
    + 
    +
    +
    +
    +
    +
    +# trajectory.proto
    +
    +
    +
    +
    +
    +### SE2Trajectory
    +
    +A 2D pose trajectory, which specified multiple points and the desired times the robot should
    +reach these points.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| points | [SE2TrajectoryPoint](#bosdyn.api.SE2TrajectoryPoint) | The points in trajectory |
    +| reference_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | All trajectories specify times relative to this reference time. The reference time should be in robot clock. If this field is not included, this time will be the receive time of the command. |
    +| interpolation | [PositionalInterpolation](#bosdyn.api.PositionalInterpolation) | Parameters for how trajectories will be interpolated on robot. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SE2TrajectoryPoint
    +
    +A SE2 pose that can be used as a point within a trajectory.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| pose | [SE2Pose](#bosdyn.api.SE2Pose) | Required pose the robot will try and achieve. |
    +| time_since_reference | [google.protobuf.Duration](#google.protobuf.Duration) | The duration to reach the point relative to the trajectory reference time. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SE3Trajectory
    +
    +A 3D pose trajectory, which specified multiple poses (and velocities for each pose)
    +and the desired times the robot should reach these points.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| points | [SE3TrajectoryPoint](#bosdyn.api.SE3TrajectoryPoint) | The points in trajectory |
    +| reference_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | All trajectories specify times relative to this reference time. The reference time should be in robot clock. If this field is not included, this time will be the receive time of the command. |
    +| pos_interpolation | [PositionalInterpolation](#bosdyn.api.PositionalInterpolation) | Parameters for how trajectories will be interpolated on robot. |
    +| ang_interpolation | [AngularInterpolation](#bosdyn.api.AngularInterpolation) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### SE3TrajectoryPoint
    +
    +A SE3 pose and velocity that can be used as a point within a trajectory.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| pose | [SE3Pose](#bosdyn.api.SE3Pose) | Required pose the robot will try and achieve. |
    +| velocity | [SE3Velocity](#bosdyn.api.SE3Velocity) | Optional velocity (linear and angular) the robot will try and achieve. |
    +| time_since_reference | [google.protobuf.Duration](#google.protobuf.Duration) | The duration to reach the point relative to the trajectory reference time. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ScalarTrajectory
    +
    +A Point trajectory.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| points | [ScalarTrajectoryPoint](#bosdyn.api.ScalarTrajectoryPoint) | The points in trajectory |
    +| reference_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | All trajectories specify times relative to this reference time. The reference time should be in robot clock. If this field is not included, this time will be the receive time of the command. |
    +| interpolation | [PositionalInterpolation](#bosdyn.api.PositionalInterpolation) | Parameters for how trajectories will be interpolated on robot. (Note: ignored for ClawGripperCommand.Request, which will automatically select between cubic interpolation or a minimum time trajectory) |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ScalarTrajectoryPoint
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| point | [double](#double) | Required position at the trajectory point's reference time. |
    +| velocity | [google.protobuf.DoubleValue](#google.protobuf.DoubleValue) | Optional speed at the trajectory point's reference time. |
    +| time_since_reference | [google.protobuf.Duration](#google.protobuf.Duration) | The duration to reach the point relative to the trajectory reference time. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Vec3Trajectory
    +
    +A 3D point trajectory, described by 3D points, a starting and ending velocity, and
    +a reference time.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| points | [Vec3TrajectoryPoint](#bosdyn.api.Vec3TrajectoryPoint) | The points in trajectory. |
    +| reference_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | All trajectories specify times relative to this reference time. The reference time should be in robot clock. If this field is not included, this time will be the receive time of the command. |
    +| pos_interpolation | [PositionalInterpolation](#bosdyn.api.PositionalInterpolation) | Parameters for how trajectories will be interpolated on robot. |
    +| starting_velocity | [Vec3](#bosdyn.api.Vec3) | Velocity at the starting point of the trajectory. |
    +| ending_velocity | [Vec3](#bosdyn.api.Vec3) | Velocity at the ending point of the trajectory. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### Vec3TrajectoryPoint
    +
    +A 3D point (and linear velocity) that can be used as a point within a trajectory.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| point | [Vec3](#bosdyn.api.Vec3) | The point 3D position. |
    +| linear_speed | [double](#double) | These are all optional. If nothing is specified, good defaults will be chosen server-side. |
    +| time_since_reference | [google.protobuf.Duration](#google.protobuf.Duration) | The duration to reach the point relative to the trajectory reference time. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### WrenchTrajectory
    +
    +A time-based trajectories of wrenches.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| points | [WrenchTrajectoryPoint](#bosdyn.api.WrenchTrajectoryPoint) | The wrenches in the trajectory |
    +| reference_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | All trajectories specify times relative to this reference time. The reference time should be in robot clock. If this field is not included, this time will be the receive time of the command. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### WrenchTrajectoryPoint
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| wrench | [Wrench](#bosdyn.api.Wrench) | The wrench to apply at this point in time. |
    +| time_since_reference | [google.protobuf.Duration](#google.protobuf.Duration) | The duration to reach the point relative to the trajectory reference time. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### AngularInterpolation
    +
    +Parameters for how angular trajectories will be interpolated on robot.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| ANG_INTERP_UNKNOWN | 0 | Unknown interpolation, do not use. |
    +| ANG_INTERP_LINEAR | 1 | Linear interpolation for angular data. |
    +| ANG_INTERP_CUBIC_EULER | 2 | Cubic interpolation (using Euler method) for angular data. |
    +
    +
    +
    +
    +
    +### PositionalInterpolation
    +
    +Parameters for how positional trajectories will be interpolated on robot.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| POS_INTERP_UNKNOWN | 0 | Unknown interpolation, do not use. |
    +| POS_INTERP_LINEAR | 1 | Linear interpolation for positional data. |
    +| POS_INTERP_CUBIC | 2 | Cubic interpolation for positional data. |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# world_object.proto
    +
    +
    +
    +
    +
    +### AprilTagProperties
    +
    +World object properties describing a fiducial object.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| tag_id | [int32](#int32) | Consistent integer id associated with a given apriltag. April Tag detections will be from the tag family 36h11. |
    +| dimensions | [Vec2](#bosdyn.api.Vec2) | Apriltag size in meters, where x is the row/width length and y is the height/col length of the tag |
    +| frame_name_fiducial | [string](#string) | The frame name for the raw version of this fiducial. This will be included in the transform snapshot. |
    +| fiducial_pose_status | [AprilTagProperties.AprilTagPoseStatus](#bosdyn.api.AprilTagProperties.AprilTagPoseStatus) | Status of the pose estimation of the unfiltered fiducial frame. |
    +| frame_name_fiducial_filtered | [string](#string) | The frame name for the filtered version of this fiducial. This will be included in the transform snapshot. |
    +| fiducial_filtered_pose_status | [AprilTagProperties.AprilTagPoseStatus](#bosdyn.api.AprilTagProperties.AprilTagPoseStatus) | Status of the pose estimation of the filtered fiducial frame. |
    +| frame_name_camera | [string](#string) | The frame name for the camera that detected this fiducial. |
    +| detection_covariance | [SE3Covariance](#bosdyn.api.SE3Covariance) | A 6 x 6 Covariance matrix representing the marginal uncertainty of the last detection. The rows/columns are: rx, ry, rz, tx, ty, tz which represent incremental rotation and translation along the x, y, and z axes of the given frame, respectively. This is computed using the Jacobian of the pose estimation algorithm. |
    +| detection_covariance_reference_frame | [string](#string) | The frame that the detection covariance is expressed in. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### BoundingBoxProperties
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| size_ewrt_frame | [Vec3](#bosdyn.api.Vec3) | An Oriented Bounding Box, with position and orientation at the frame provided in the transforms snapshot.
    +
    +The size of the box is expressed with respect to the frame. |
    +| frame | [string](#string) | Frame the size is expressed with respect to. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DockProperties
    +
    +World object properties describing a dock
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| dock_id | [uint32](#uint32) | Consistent id associated with a given dock. |
    +| type | [docking.DockType](#bosdyn.api.docking.DockType) | Type of dock. |
    +| frame_name_dock | [string](#string) | The frame name for the location of dock origin. This will be included in the transform snapshot. |
    +| unavailable | [bool](#bool) | Availability if the dock can be used |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DrawableArrow
    +
    +A directed arrow drawing object.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| direction | [Vec3](#bosdyn.api.Vec3) |  |
    +| radius | [double](#double) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DrawableBox
    +
    +A three dimensional box drawing object.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| size | [Vec3](#bosdyn.api.Vec3) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DrawableCapsule
    +
    +A oval-like capsule drawing object.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| direction | [Vec3](#bosdyn.api.Vec3) |  |
    +| radius | [double](#double) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DrawableCylinder
    +
    +A cylinder drawing object.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| direction | [Vec3](#bosdyn.api.Vec3) |  |
    +| radius | [double](#double) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DrawableFrame
    +
    +A coordinate frame drawing object, describing how large to render the arrows.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| arrow_length | [double](#double) |  |
    +| arrow_radius | [double](#double) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DrawableLineStrip
    +
    +A line strip drawing object.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| points | [Vec3](#bosdyn.api.Vec3) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DrawablePoints
    +
    +A set of points drawing object.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| points | [Vec3](#bosdyn.api.Vec3) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DrawableProperties
    +
    +The drawing and visualization information for a world object.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| color | [DrawableProperties.Color](#bosdyn.api.DrawableProperties.Color) | Color of the object. |
    +| label | [string](#string) | Label to be drawn at the origin of the object. |
    +| wireframe | [bool](#bool) | Drawn objects in wireframe. |
    +| frame | [DrawableFrame](#bosdyn.api.DrawableFrame) | A drawable frame (oneof drawable field). |
    +| sphere | [DrawableSphere](#bosdyn.api.DrawableSphere) | A drawable sphere (oneof drawable field). |
    +| box | [DrawableBox](#bosdyn.api.DrawableBox) | A drawable box (oneof drawable field). |
    +| arrow | [DrawableArrow](#bosdyn.api.DrawableArrow) | A drawable arrow (oneof drawable field). |
    +| capsule | [DrawableCapsule](#bosdyn.api.DrawableCapsule) | A drawable capsule (oneof drawable field). |
    +| cylinder | [DrawableCylinder](#bosdyn.api.DrawableCylinder) | A drawable cylinder (oneof drawable field). |
    +| linestrip | [DrawableLineStrip](#bosdyn.api.DrawableLineStrip) | A drawable linestrip (oneof drawable field). |
    +| points | [DrawablePoints](#bosdyn.api.DrawablePoints) | A drawable set of points (oneof drawable field). |
    +| frame_name_drawable | [string](#string) | The frame name for the drawable object. This will optionally be included in the frame tree snapshot. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DrawableProperties.Color
    +
    +RGBA values for color ranging from [0,255] for R/G/B, and [0,1] for A.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| r | [int32](#int32) | Red value ranging from [0,255]. |
    +| g | [int32](#int32) | Green value ranging from [0,255]. |
    +| b | [int32](#int32) | Blue value ranging from [0,255]. |
    +| a | [double](#double) | Alpha (transparency) value ranging from [0,1]. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### DrawableSphere
    +
    +A sphere drawing object.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| radius | [double](#double) |  |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ImageProperties
    +
    +World object properties describing image coordinates associated with an object or scene.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| camera_source | [string](#string) | Camera Source of such as "back", "frontleft", etc. |
    +| coordinates | [Polygon](#bosdyn.api.Polygon) | Image coordinates of the corners of a polygon (pixels of x[row], y[col]) in either clockwise/counter clockwise order |
    +| keypoints | [KeypointSet](#bosdyn.api.KeypointSet) | A set of keypoints and their associated metadata. |
    +| image_source | [ImageSource](#bosdyn.api.ImageSource) | Camera parameters. |
    +| image_capture | [ImageCapture](#bosdyn.api.ImageCapture) | Image that produced the data. |
    +| frame_name_image_coordinates | [string](#string) | Frame name for the object described by image coordinates. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListWorldObjectRequest
    +
    +The ListWorldObject request message, which can optionally include filters for the object type or timestamp.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header |
    +| object_type | [WorldObjectType](#bosdyn.api.WorldObjectType) | Optional filters to apply to the world object request Specific type of object; can request multiple different properties |
    +| timestamp_filter | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Timestamp to filter objects based on. The time should be in robot time All objects with header timestamps after (>) timestamp_filter will be returned |
    +
    +
    +
    +
    +
    +
    +
    +
    +### ListWorldObjectResponse
    +
    +The ListWorldObject response message, which contains all of the current world objects in the
    +robot's perception scene.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header |
    +| world_objects | [WorldObject](#bosdyn.api.WorldObject) | The currently perceived world objects. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### MutateWorldObjectRequest
    +
    +The MutateWorldObject request message, which specifies the type of mutation and which object
    +the mutation should be applied to.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [RequestHeader](#bosdyn.api.RequestHeader) | Common request header |
    +| mutation | [MutateWorldObjectRequest.Mutation](#bosdyn.api.MutateWorldObjectRequest.Mutation) | The mutation for this request. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### MutateWorldObjectRequest.Mutation
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| action | [MutateWorldObjectRequest.Action](#bosdyn.api.MutateWorldObjectRequest.Action) | The action (add, change, or delete) to be applied to a world object. |
    +| object | [WorldObject](#bosdyn.api.WorldObject) | World object to be mutated. If an object is being changed/deleted, then the world object id must match a world object id known by the service. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### MutateWorldObjectResponse
    +
    +The MutateWorldObject response message, which includes the world object id for the object that
    +the mutation was applied to if the request succeeds.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| header | [ResponseHeader](#bosdyn.api.ResponseHeader) | Common response header |
    +| status | [MutateWorldObjectResponse.Status](#bosdyn.api.MutateWorldObjectResponse.Status) | Return status for the request. |
    +| mutated_object_id | [int32](#int32) | ID set by the world object service for the mutated object |
    +
    +
    +
    +
    +
    +
    +
    +
    +### RayProperties
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| ray | [Ray](#bosdyn.api.Ray) | Ray, usually pointing from the camera to the object. |
    +| frame | [string](#string) | Frame the ray is expressed with respect to. |
    +
    +
    +
    +
    +
    +
    +
    +
    +### WorldObject
    +
    +The world object message is used to describe different objects seen by a robot. It contains information
    +about the properties of the object in addition to a unique id and the transform snapshot.
    +The world object uses "properties" to describe different traits about the object, such as image coordinates
    +associated with the camera the object was detected in. A world object can have multiple different properties
    +that are all associated with the single object.
    +
    +
    +
    +| Field | Type | Description |
    +| ----- | ---- | ----------- |
    +| id | [int32](#int32) | Unique integer identifier that will be consistent for the duration of a robot's battery life The id is set internally by the world object service. |
    +| name | [string](#string) | A human readable name for the world object. Note that this differs from any frame_name's associated with the object (since there can be multiple frames describing a single object). |
    +| acquisition_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | Time in robot time clock at which this object was most recently detected and valid. |
    +| transforms_snapshot | [FrameTreeSnapshot](#bosdyn.api.FrameTreeSnapshot) | A tree-based collection of transformations, which will include the transformations to each of the returned world objects in addition to transformations to the common frames ("vision", "body", "odom"). All transforms within the snapshot are at the acquisition time of the world object. Note that each object's frame names are defined within the properties submessage. For example, the apriltag frame name is defined in the AprilTagProperties message as "frame_name_fiducial" |
    +| drawable_properties | [DrawableProperties](#bosdyn.api.DrawableProperties) | The drawable properties describe geometric shapes associated with an object. |
    +| apriltag_properties | [AprilTagProperties](#bosdyn.api.AprilTagProperties) | The apriltag properties describe any fiducial identifying an object. |
    +| image_properties | [ImageProperties](#bosdyn.api.ImageProperties) | The image properties describe any camera and image coordinates associated with an object. |
    +| dock_properties | [DockProperties](#bosdyn.api.DockProperties) | Properties describing a dock |
    +| ray_properties | [RayProperties](#bosdyn.api.RayProperties) | A ray pointing at the object. Useful in cases where position is unknown but direction is known. |
    +| bounding_box_properties | [BoundingBoxProperties](#bosdyn.api.BoundingBoxProperties) | Bounding box in the world, oriented at the location provided in the transforms_snapshot. |
    +| additional_properties | [google.protobuf.Any](#google.protobuf.Any) | An extra field for application-specific object properties. |
    +
    +
    +
    +
    +
    + 
    +
    +
    +
    +
    +### AprilTagProperties.AprilTagPoseStatus
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 |  |
    +| STATUS_OK | 1 | No known issues with the pose estimate. |
    +| STATUS_AMBIGUOUS | 2 | The orientation of the tag is ambiguous. |
    +| STATUS_HIGH_ERROR | 3 | The pose may be unreliable due to high reprojection error. |
    +
    +
    +
    +
    +
    +### MutateWorldObjectRequest.Action
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| ACTION_UNKNOWN | 0 | Invalid action. |
    +| ACTION_ADD | 1 | Add a new object. |
    +| ACTION_CHANGE | 2 | Change an existing objected (ID'd by integer ID number). This is only allowed to change objects added by the API-user, and not objects detected by Spot's perception system. |
    +| ACTION_DELETE | 3 | Delete the object, ID'd by integer ID number. This is only allowed to change objects added by the API-user, and not objects detected by Spot's perception system. |
    +
    +
    +
    +
    +
    +### MutateWorldObjectResponse.Status
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| STATUS_UNKNOWN | 0 | Status of request is unknown. Check the status code of the response header. |
    +| STATUS_OK | 1 | Request was accepted; GetObjectListResponse must still be checked to verify the changes. |
    +| STATUS_INVALID_MUTATION_ID | 2 | The mutation object's ID is unknown such that the service could not recognize this object. This error applies to the CHANGE and DELETE actions, since it must identify the object by it's id number given by the service. |
    +| STATUS_NO_PERMISSION | 3 | The mutation request is not allowed because it is attempting to change or delete an object detected by Spot's perception system. |
    +
    +
    +
    +
    +
    +### WorldObjectType
    +
    +A type for the world object, which is associated with whatever properties the world object includes. This can
    +be used to request specific kinds of objects; for example, a request for only fiducials.
    +
    +
    +
    +| Name | Number | Description |
    +| ---- | ------ | ----------- |
    +| WORLD_OBJECT_UNKNOWN | 0 |  |
    +| WORLD_OBJECT_DRAWABLE | 1 |  |
    +| WORLD_OBJECT_APRILTAG | 2 |  |
    +| WORLD_OBJECT_IMAGE_COORDINATES | 5 |  |
    +| WORLD_OBJECT_DOCK | 6 |  |
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +
    +# world_object_service.proto
    +
    +
    + 
    +
    + 
    +
    + 
    +
    +
    +
    +
    +### WorldObjectService
    +
    +The world object service provides a way to track and store objects detected in the world around the robot.
    +
    +
    +
    +| Method Name | Request Type | Response Type | Description |
    +| ----------- | ------------ | ------------- | ------------|
    +| ListWorldObjects | [ListWorldObjectRequest](#bosdyn.api.ListWorldObjectRequest) | [ListWorldObjectResponse](#bosdyn.api.ListWorldObjectResponse) | Request a list of all the world objects in the robot's perception scene. |
    +| MutateWorldObjects | [MutateWorldObjectRequest](#bosdyn.api.MutateWorldObjectRequest) | [MutateWorldObjectResponse](#bosdyn.api.MutateWorldObjectResponse) | Mutate (add, change, or delete) the world objects. |
    +
    + 
    +
    +
    +
    +
    +# Standard Types
    +
    +| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby |
    +| ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- |
    +|  double |  | double | double | float | float64 | double | float | Float |
    +|  float |  | float | float | float | float32 | float | float | Float |
    +|  int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
    +|  int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum |
    +|  uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) |
    +|  uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) |
    +|  sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
    +|  sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum |
    +|  fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) |
    +|  fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum |
    +|  sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
    +|  sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum |
    +|  bool |  | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass |
    +|  string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) |
    +|  bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) |
    +
    diff --git a/protos/bosdyn/api/robot_command.proto b/protos/bosdyn/api/robot_command.proto
    index f347fb218..765f01563 100644
    --- a/protos/bosdyn/api/robot_command.proto
    +++ b/protos/bosdyn/api/robot_command.proto
    @@ -118,6 +118,9 @@ message RobotCommandFeedbackResponse {
         // Common response header.
         ResponseHeader header = 1;
     
    +    // Details about how the lease was used.
    +    LeaseUseResult lease_use_result = 5;
    +
         enum Status {
             // Status enum is DEPRECATED as of 2.1.0. Behavior execution is in an unknown / unexpected state.
             STATUS_UNKNOWN = 0;
    diff --git a/protos/bosdyn/api/robot_id.proto b/protos/bosdyn/api/robot_id.proto
    index 2642a69d9..a8f7f21a1 100644
    --- a/protos/bosdyn/api/robot_id.proto
    +++ b/protos/bosdyn/api/robot_id.proto
    @@ -8,12 +8,13 @@ syntax = "proto3";
     
     package bosdyn.api;
     
    -option java_outer_classname = "RobotIdProto";
    -
     import "bosdyn/api/header.proto";
     import "bosdyn/api/parameter.proto";
     import "google/protobuf/timestamp.proto";
     
    +option go_package = "bosdyn/api";
    +option java_outer_classname = "RobotIdProto";
    +
     // Robot identity information, which should be static while robot is powered-on.
     message RobotId {
         // A unique string identifier for the particular robot.
    @@ -87,4 +88,4 @@ message RobotIdRequest {
     message RobotIdResponse {
         ResponseHeader header = 1;  // Common request/response header.
         RobotId robot_id = 2;       // The requested RobotId information.
    -}
    \ No newline at end of file
    +}
    diff --git a/protos/bosdyn/api/robot_state.proto b/protos/bosdyn/api/robot_state.proto
    index 04c45d858..f1ec1a3e3 100644
    --- a/protos/bosdyn/api/robot_state.proto
    +++ b/protos/bosdyn/api/robot_state.proto
    @@ -52,10 +52,11 @@ message HardwareConfiguration {
     
         // Set of power features that are compatible with the robot hardware.
         // See power.proto for the associated requests.
    -    bool can_power_command_request_off_robot = 2;          // Turn off the robot. Same as physical switch.
    -    bool can_power_command_request_cycle_robot = 3;        // Power cycle the robot. Same as physical switch.
    -    bool can_power_command_request_payload_ports = 4;      // Control power to the payload ports.
    -    bool can_power_command_request_wifi_radio = 5;        // Control power to the hardware Wi-Fi radio.
    +
    +    bool can_power_command_request_off_robot = 2;        // Turn off the robot. Same as physical switch.
    +    bool can_power_command_request_cycle_robot = 3;      // Power cycle the robot. Same as physical switch.
    +    bool can_power_command_request_payload_ports = 4;    // Control power to the payload ports.
    +    bool can_power_command_request_wifi_radio = 5;       // Control power to the hardware Wi-Fi radio.
     
     }
     
    @@ -516,6 +517,35 @@ message FootState {
         }
         // Is the foot in contact with the ground?
         Contact contact = 2;
    +
    +    // Foot specific terrain data. Data may not be valid if the contact state is
    +    // not CONTACT_MADE.
    +    message TerrainState {
    +        // Estimated ground coefficient of friction for this foot.
    +        double ground_mu_est = 1;
    +
    +        // Reference frame name for vector data.
    +        string frame_name = 2;
    +
    +        // Foot slip distance rt named frame
    +        Vec3 foot_slip_distance_rt_frame = 3;
    +
    +        // Foot slip velocity rt named frame
    +        Vec3 foot_slip_velocity_rt_frame = 4;
    +
    +        // Ground contact normal rt named frame
    +        Vec3 ground_contact_normal_rt_frame = 5;
    +
    +        // Mean penetration (meters) of the foot below the ground visual
    +        // surface. For penetrable terrains (gravel/sand/grass etc.) these values are
    +        // positive. Negative values would indicate potential odometry issues.
    +        double visual_surface_ground_penetration_mean = 6;
    +
    +        // Standard deviation of the visual surface ground penetration.
    +        double visual_surface_ground_penetration_std = 7;
    +    }
    +
    +    TerrainState terrain = 3;
     }
     
     /// Additional state published if an arm is attached to the robot.
    @@ -545,6 +575,23 @@ message ManipulatorState {
         // Again, the linear velocity is applied at the origin of the hand frame.
         SE3Velocity velocity_of_hand_in_odom = 15;
     
    +    // The stowing behavior is modified as a function of the Carry State.  If holding an item, the
    +    // stowing behavior will be modified as follows:
    +    //  NOT_CARRIABLE - The arm will not stow, instead entering stop
    +    //  CARRIABLE - The arm will not stow, instead entering stop
    +    //  CARRIABLE_AND_STOWABLE - The arm will stow while continuing to grasp the item
    +    // The comms loss behavior of the arm is also modified as follows:
    +    //  NOT_CARRIABLE - The arm will release the item and stow
    +    //  CARRIABLE - The arm will not stow, instead entering stop
    +    //  CARRIABLE_AND_STOWABLE - The arm will stow while continuing to grasp the item
    +    enum CarryState {
    +        CARRY_STATE_UNKNOWN = 0;
    +        CARRY_STATE_NOT_CARRIABLE = 1;
    +        CARRY_STATE_CARRIABLE = 2;
    +        CARRY_STATE_CARRIABLE_AND_STOWABLE = 3;
    +    }
    +    CarryState carry_state = 16;
    +
         // Previous fields in the protobuf that are now reserved.
         reserved 1, 2, 3, 4, 5, 7, 8, 10, 11;
     }
    @@ -556,7 +603,7 @@ message ServiceFaultState {
         // Currently active faults
         repeated ServiceFault faults = 1;
     
    -    // Inactive faults that cleared within the last 10 minutes
    +    // Service faults that have been cleared. Acts as a ring buffer with size of 50.
         repeated ServiceFault historical_faults = 2;
     
         // Aggregated service fault data. Maps attribute string to highest severity level
    @@ -631,3 +678,40 @@ message RobotLinkModelResponse {
         // The requested RobotState skeleton obj model.
         Skeleton.Link.ObjModel link_model = 2;
     }
    +
    +// Keeps track of why the robot is not able to drive autonomously.
    +message RobotImpairedState {
    +    // If the robot is stopped due to being impaired, this is the reason why.
    +    enum ImpairedStatus {
    +        // Unknown/unexpected error.
    +        IMPAIRED_STATUS_UNKNOWN = 0;
    +        // The robot is able to drive.
    +        IMPAIRED_STATUS_OK = 1;
    +        // The autonomous system does not have any data from the robot state service.
    +        IMPAIRED_STATUS_NO_ROBOT_DATA = 2;
    +        // There is a system fault which caused the robot to stop. See system_fault for details.
    +        IMPAIRED_STATUS_SYSTEM_FAULT = 3;
    +        // The robot's motors are not powered on.
    +        IMPAIRED_STATUS_NO_MOTOR_POWER = 4;
    +        // The autonomous system is expected to have a remote point cloud (e.g. a LIDAR), but this is not working.
    +        IMPAIRED_STATUS_REMOTE_CLOUDS_NOT_WORKING = 5;
    +        // A remote service the autonomous system depends on is not working.
    +        IMPAIRED_STATUS_SERVICE_FAULT = 6;
    +        // A behavior fault caused the robot to stop. See behavior_faults for details.
    +        IMPAIRED_STATUS_BEHAVIOR_FAULT = 7;
    +    }
    +    // If the status is ROBOT_IMPAIRED, this is specifically why the robot is impaired.
    +    ImpairedStatus impaired_status = 1;
    +
    +    // If impaired_status is STATUS_SYSTEM_FAULT, these are the faults which caused the robot to stop.
    +    repeated bosdyn.api.SystemFault system_faults = 2;
    +
    +    // If impaired_status is STATUS_SERVICE_FAULT, these are the service faults which caused
    +    // the robot to stop.
    +    repeated bosdyn.api.ServiceFault service_faults = 3;
    +
    +    // If impaired_status is STATUS_BEHAVIOR_FAULT, these are the behavior faults which caused
    +    // the robot to stop.
    +    repeated bosdyn.api.BehaviorFault behavior_faults = 4;
    +}
    +
    diff --git a/protos/bosdyn/api/sparse_features.proto b/protos/bosdyn/api/sparse_features.proto
    new file mode 100644
    index 000000000..4790183cc
    --- /dev/null
    +++ b/protos/bosdyn/api/sparse_features.proto
    @@ -0,0 +1,66 @@
    +// Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +//
    +// Downloading, reproducing, distributing or otherwise using the SDK Software
    +// is subject to the terms and conditions of the Boston Dynamics Software
    +// Development Kit License (20191101-BDSDK-SL).
    +
    +syntax = "proto3";
    +
    +package bosdyn.api;
    +
    +option java_outer_classname = "SparseFeaturesProto";
    +
    +import "bosdyn/api/geometry.proto";
    +
    +// A point of interest in an image expressed as a pixel coordinate with associated metadata.
    +message Keypoint {
    +    // The image pixel coordinates of the keypoint.
    +    Vec2 coordinates = 2;
    +    // A binary descriptor representing the keypoint.
    +    bytes binary_descriptor = 3;
    +    // The score of this keypoint from the underlying keypoint detector, if applicable.
    +    float score = 4;
    +    // The diameter in pixels of the local neighborhood used to construct the descriptor.
    +    float size = 5;
    +    // The orientation of the keypoint, if applicable.
    +    float angle = 6;
    +}
    +
    +// A set of keypoints detected in a single image.
    +message KeypointSet {
    +    // A set of detected keypoints and associated metadata.
    +    repeated Keypoint keypoints = 2;
    +
    +    enum KeypointType {
    +        KEYPOINT_UNKNOWN = 0;
    +        // Keypoints that consist only of image coordinates. Simple keypoints do not have
    +        // descriptors.
    +        KEYPOINT_SIMPLE = 1;
    +        // Keypoints detected by the ORB feature extraction algorithm (Oriented FAST and Rotated
    +        // BRIEF.)
    +        KEYPOINT_ORB = 2;
    +    }
    +    // The algorithm used to compute this keypoint and its descriptor.
    +    KeypointType type = 3;
    +}
    +
    +message Match {
    +    // The index in the reference KeypointSet of the keypoint in the matching pair.
    +    int32 reference_index = 2;
    +    // The index in the live KeypointSet of the keypoint in the matching pair.
    +    int32 live_index = 3;
    +    // The distance in descriptor space between the two keypoints.
    +    float distance = 4;
    +}
    +
    +// A pair of keypoint sets containing only features in common that have been matched.
    +message KeypointMatches {
    +    // The set of common keypoints in a first ("reference") image.
    +    KeypointSet reference_keypoints = 2;
    +
    +    // The set of common keypoints in a second ("live") image.
    +    KeypointSet live_keypoints = 3;
    +
    +    // Indices of pairs of matches in the two KeypointSets and their distance measure.
    +    repeated Match matches = 4;
    +}
    diff --git a/protos/bosdyn/api/spot/door.proto b/protos/bosdyn/api/spot/door.proto
    index f062cfcf1..800f535ac 100644
    --- a/protos/bosdyn/api/spot/door.proto
    +++ b/protos/bosdyn/api/spot/door.proto
    @@ -128,12 +128,29 @@ message DoorCommand {
             HandleType handle_type = 3;
         }
     
    +    // Open doors that do not require a grasp, just a push. This could be a door with no latching
    +    // mechanism that just requires a push, or a door with a pushbar.
    +    // The robot will automatically push the door open and walk through.
    +    message AutoPushCommand {
    +        // The name of the frame that the following fields are expressed in.
    +        string frame_name = 1;
    +
    +        // The point that the robot will push on.
    +        Vec3 push_point_in_frame = 2;
    +
    +        // The side of the hinge with respect to the robot when facing the door.
    +        HingeSide hinge_side = 3;
    +    }
    +
         message Request {
             oneof command {
                 AutoGraspCommand auto_grasp_command = 10;
     
                 WarmstartCommand warmstart_command = 11;
    +
    +            AutoPushCommand auto_push_command = 12;
             }
    +
         }
     
         message Feedback {
    diff --git a/protos/bosdyn/api/spot/robot_command.proto b/protos/bosdyn/api/spot/robot_command.proto
    index 7f9ef2c98..2a6d33741 100644
    --- a/protos/bosdyn/api/spot/robot_command.proto
    +++ b/protos/bosdyn/api/spot/robot_command.proto
    @@ -93,6 +93,7 @@ message MobilityParams {
     
         // Disable the secondary nearmap-based cliff avoidance that runs while on stairs.
         bool disable_nearmap_cliff_avoidance = 12;
    +
     }
     
     // Parameters for offsetting the body from the normal default.
    @@ -128,6 +129,11 @@ message ObstacleParams {
       // Desired padding around the body to use when attempting to avoid obstacles.
       // Described in meters. Must be >= 0.
       double obstacle_avoidance_padding = 4;
    +  // Prevent the robot body from raising above nominal height to avoid lower-leg collisions with
    +  // the terrain.
    +  bool disable_vision_foot_obstacle_body_assist = 5;
    +  // Use vision to make the robot avoid stepping into negative obstacles?
    +  bool disable_vision_negative_obstacles = 6;
     
     }
     
    @@ -138,8 +144,26 @@ message TerrainParams {
         // necessary on the robot side. Best suggested values lie in the range between 0.4 and 0.8
         // (which is the robot's default.)
         google.protobuf.DoubleValue ground_mu_hint = 2;
    -    // When true, the robot will assume the ground below it is made of grated metal.
    -    bool enable_grated_floor = 3;
    +
    +    // *** Deprecation Warning ***
    +    // DEPRECATED as of 3.0.0: The boolean field has been replaced by the field grated_surfaces_mode
    +    // The following field will be deprecated and moved to 'reserved' in a future release.
    +    bool enable_grated_floor = 3 [deprecated = true];
    +
    +    // Options for Grated Surfaces Mode. When Grated Surfaces Mode is on, the robot assumes the
    +    // ground below it is made of grated metal or other repeated pattern. When on, the robot will
    +    // make assumptions about the environment structure and more aggressively filter noise in
    +    // perception data.
    +    enum GratedSurfacesMode {
    +        GRATED_SURFACES_MODE_UNKNOWN = 0; // Invalid; do not use.
    +        GRATED_SURFACES_MODE_OFF = 1;
    +        GRATED_SURFACES_MODE_ON = 2;
    +        GRATED_SURFACES_MODE_AUTO = 3;    // Robot will automatically turn mode on or off
    +    }
    +    // The selected option for grated surfaces mode
    +    GratedSurfacesMode grated_surfaces_mode = 4;
    +
    +
     }
     
     // External Force on robot body parameters. This is a beta feature and still can have some odd behaviors.
    diff --git a/protos/bosdyn/api/spot/spot_check.proto b/protos/bosdyn/api/spot/spot_check.proto
    index 0c0fb1c70..5a7e3a18b 100644
    --- a/protos/bosdyn/api/spot/spot_check.proto
    +++ b/protos/bosdyn/api/spot/spot_check.proto
    @@ -339,6 +339,12 @@ message CameraCalibrationFeedbackResponse {
             STATUS_TARGET_UPSIDE_DOWN = 13;
             // Calibration routine has never been run. No feedback to give.
             STATUS_NEVER_RUN = 10;
    +        // One of the cameras is not detected on the USB bus.
    +        STATUS_CAMERA_NOT_DETECTED = 15;
    +        // Failed to write intrinsic calibration.
    +        STATUS_INTRINSIC_WRITE_FAILED = 16;
    +        // Failed to write extrinsic calibration.
    +        STATUS_EXTRINSIC_WRITE_FAILED = 17;
         }
         // Status of camera calibration procedure.
         Status status = 2;
    diff --git a/protos/bosdyn/api/spot_cam/logging.proto b/protos/bosdyn/api/spot_cam/logging.proto
    index 65d59e00d..741cce8e7 100644
    --- a/protos/bosdyn/api/spot_cam/logging.proto
    +++ b/protos/bosdyn/api/spot_cam/logging.proto
    @@ -124,6 +124,7 @@ message RetrieveResponse {
     
     // Retrieve the binary data associated with a log point, with no processing applied.
     // Storing a panorama will retrieve tiled individual images.
    +// For IR, the temperature at each pixel is 0.1 * the int value in Kelvin.
     message RetrieveRawDataRequest {
         // Common request header.
         bosdyn.api.RequestHeader header = 1;
    diff --git a/protos/bosdyn/api/spot_cam/ptz.proto b/protos/bosdyn/api/spot_cam/ptz.proto
    index fefe329a7..5385f54a2 100644
    --- a/protos/bosdyn/api/spot_cam/ptz.proto
    +++ b/protos/bosdyn/api/spot_cam/ptz.proto
    @@ -25,6 +25,7 @@ message PtzDescription {
             google.protobuf.FloatValue max = 2; // Units depend on the axis being controlled.
         }
     
    +    // If a limit is not set, all positions are valid
         Limits pan_limit = 2; // Limits in degrees.
         Limits tilt_limit = 3; // Limits in degrees.
         Limits zoom_limit = 4; // Limits in zoom level.
    @@ -139,3 +140,4 @@ message InitializeLensResponse {
         // Common response header.
         bosdyn.api.ResponseHeader header = 1;
     }
    +
    diff --git a/protos/bosdyn/api/spot_cam/service.proto b/protos/bosdyn/api/spot_cam/service.proto
    index 1fceeb052..d76e94f6a 100644
    --- a/protos/bosdyn/api/spot_cam/service.proto
    +++ b/protos/bosdyn/api/spot_cam/service.proto
    @@ -88,6 +88,7 @@ service MediaLogService {
         rpc ListLogpoints (ListLogpointsRequest) returns (stream ListLogpointsResponse);
         //SetPassphrase sets the eCryptFS passphrase used by the filesystem.
         //there is no symmetry here, because key material is write-only
    +    //This rpc is now deprecated as of the switch from EXT4 to NTFS and returns UnimplementedError
         rpc SetPassphrase (SetPassphraseRequest) returns (SetPassphraseResponse);
     }
     
    @@ -102,6 +103,7 @@ service PtzService {
         rpc ListPtz (ListPtzRequest) returns (ListPtzResponse);
         // Reinitializes PTZ autofocus
         rpc InitializeLens (InitializeLensRequest) returns (InitializeLensResponse);
    +
     }
     
     // Upload and play sounds over the SpotCam's speakers.
    diff --git a/protos/bosdyn/api/stairs.proto b/protos/bosdyn/api/stairs.proto
    new file mode 100644
    index 000000000..87e243210
    --- /dev/null
    +++ b/protos/bosdyn/api/stairs.proto
    @@ -0,0 +1,57 @@
    +// Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +//
    +// Downloading, reproducing, distributing or otherwise using the SDK Software
    +// is subject to the terms and conditions of the Boston Dynamics Software
    +// Development Kit License (20191101-BDSDK-SL).
    +
    +syntax = "proto3";
    +
    +package bosdyn.api;
    +
    +option java_outer_classname = "StairsProto";
    +
    +import "bosdyn/api/geometry.proto";
    +
    +message StairTransform {
    +    // The staircase origin is the bottom-center of the first rise.
    +    bosdyn.api.SE3Pose frame_tform_stairs = 1;
    +    string frame_name = 2;
    +}
    +
    +message StraightStaircase {
    +    // The staircase origin is the bottom-center of the first rise.
    +    oneof location {
    +        // It is expressed in ko frame of the from_waypoint.
    +        // This field is only used in GraphNav.
    +        bosdyn.api.SE3Pose from_ko_tform_stairs = 1;
    +
    +        // Outside GraphNav, this field specifies the stair origin.
    +        StairTransform tform = 5;
    +    }
    +
    +    // A single stair from a staircase.
    +    message Stair {
    +        // Height of each stair.
    +        float rise = 1;
    +        // Depth of each stair.
    +        float run = 2;
    +    }
    +    // Straight staircases have two landings, one at the top and one at the bottom.
    +    // Landings are areas of free space before and after the stairs, and are represented
    +    // as oriented bounding boxes.
    +    message Landing {
    +        // Pose of the landing's center relative to the stairs frame.
    +        bosdyn.api.SE3Pose stairs_tform_landing_center = 1;
    +        // The half-size of the box representing the landing in the x axis.
    +        double landing_extent_x = 2;
    +        // The half-size of the box representing the landing in the y axis.
    +        double landing_extent_y = 3;
    +    }
    +    // Each stair should be rise followed by run. The last stair will have zero run.
    +    repeated Stair stairs = 2;
    +    // The lowermost landing of the stairs. The robot will try to
    +    // align itself to the stairs while on this landing.
    +    Landing bottom_landing = 3;
    +    // The uppermost landing of the stairs.
    +    Landing top_landing = 4;
    +}
    diff --git a/protos/bosdyn/api/world_object.proto b/protos/bosdyn/api/world_object.proto
    index be41391d0..1f0bc2822 100644
    --- a/protos/bosdyn/api/world_object.proto
    +++ b/protos/bosdyn/api/world_object.proto
    @@ -13,6 +13,8 @@ option java_outer_classname = "WorldObjectProto";
     import "bosdyn/api/docking/docking.proto";
     import "bosdyn/api/geometry.proto";
     import "bosdyn/api/header.proto";
    +import "bosdyn/api/image.proto";
    +import "bosdyn/api/sparse_features.proto";
     import "google/protobuf/timestamp.proto";
     import "google/protobuf/any.proto";
     
    @@ -35,7 +37,7 @@ message WorldObject {
     
         // A tree-based collection of transformations, which will include the transformations to each
         // of the returned world objects in addition to transformations to the common frames ("vision",
    -    // "body", "odom"). All transforms within the snapshot are at the acquistion time of the world object.
    +    // "body", "odom"). All transforms within the snapshot are at the acquisition time of the world object.
         // Note that each object's frame names are defined within the properties submessage. For example,
         // the apriltag frame name is defined in the AprilTagProperties message as "frame_name_fiducial"
         FrameTreeSnapshot transforms_snapshot = 31;
    @@ -48,6 +50,11 @@ message WorldObject {
         ImageProperties image_properties = 9;
         // Properties describing a dock
         DockProperties dock_properties = 10;
    +    // A ray pointing at the object.  Useful in cases where position is unknown but direction is
    +    // known.
    +    RayProperties ray_properties = 11;
    +    // Bounding box in the world, oriented at the location provided in the transforms_snapshot.
    +    BoundingBoxProperties bounding_box_properties = 12;
         // An extra field for application-specific object properties.
         google.protobuf.Any additional_properties = 100;
     }
    @@ -149,13 +156,24 @@ message MutateWorldObjectResponse {
       int32 mutated_object_id = 4;
     }
     
    -//  World object properties describing image coordinates associated with an object.
    +//  World object properties describing image coordinates associated with an object or scene.
     message ImageProperties {
         // Camera Source of such as "back", "frontleft", etc.
         string camera_source = 1;
     
    -    // Image Coordinates (pixels of x[row], y[col]) in either clockwise/counter clockwise order
    -    Polygon coordinates = 2;
    +    oneof image_data {
    +        // Image coordinates of the corners of a polygon (pixels of x[row], y[col]) in either
    +        // clockwise/counter clockwise order
    +        Polygon coordinates = 2;
    +
    +        // A set of keypoints and their associated metadata.
    +        KeypointSet keypoints = 4;
    +    }
    +
    +    // Camera parameters.
    +    ImageSource image_source = 5;
    +    // Image that produced the data.
    +    ImageCapture image_capture = 6;
     
         // Frame name for the object described by image coordinates.
         string frame_name_image_coordinates = 3;
    @@ -169,6 +187,8 @@ message DockProperties {
       docking.DockType type = 2;
       // The frame name for the location of dock origin. This will be included in the transform snapshot.
       string frame_name_dock = 3;
    +  // Availability if the dock can be used
    +  bool unavailable = 4;
     }
     
     //  World object properties describing a fiducial object.
    @@ -218,6 +238,25 @@ message AprilTagProperties {
     
     }
     
    +message RayProperties {
    +    // Ray, usually pointing from the camera to the object.
    +    Ray ray = 1;
    +
    +    // Frame the ray is expressed with respect to.
    +    string frame = 2;
    +}
    +
    +message BoundingBoxProperties {
    +    // An Oriented Bounding Box, with position and orientation at the frame provided in the
    +    // transforms snapshot.
    +    //
    +    // The size of the box is expressed with respect to the frame.
    +    Vec3 size_ewrt_frame = 1;
    +
    +    // Frame the size is expressed with respect to.
    +    string frame = 2;
    +}
    +
     
     // The drawing and visualization information for a world object.
     message DrawableProperties {
    @@ -229,7 +268,7 @@ message DrawableProperties {
             int32 g = 2;
             // Blue value ranging from [0,255].
             int32 b = 3;
    -        // Alpha (transparancy) value ranging from [0,1].
    +        // Alpha (transparency) value ranging from [0,1].
             double a = 4;
         }
         // Color of the object.
    diff --git a/python/bosdyn-choreography-client/src/bosdyn/choreography/client/README.md b/python/bosdyn-choreography-client/src/bosdyn/choreography/client/README.md
    index f575daf4b..7987fc76d 100644
    --- a/python/bosdyn-choreography-client/src/bosdyn/choreography/client/README.md
    +++ b/python/bosdyn-choreography-client/src/bosdyn/choreography/client/README.md
    @@ -12,4 +12,6 @@ Choreography client code and interfaces for the Boston Dynamics robot API.
     
     ## Contents
     
    -* [Choreography](choreography)
    \ No newline at end of file
    +* [Choreography](choreography)
    +* [Animation File to Proto](animation_file_to_proto)
    +* [Animation File to Proto Helpers](animation_file_conversion_helpers)
    \ No newline at end of file
    diff --git a/python/bosdyn-choreography-client/src/bosdyn/choreography/client/animation_file_conversion_helpers.py b/python/bosdyn-choreography-client/src/bosdyn/choreography/client/animation_file_conversion_helpers.py
    new file mode 100644
    index 000000000..d7d934569
    --- /dev/null
    +++ b/python/bosdyn-choreography-client/src/bosdyn/choreography/client/animation_file_conversion_helpers.py
    @@ -0,0 +1,593 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""A set helpers which convert specific lines from an animation
    +file into the animation-specific protobuf messages.
    +
    +NOTE: All of these helpers are to convert specific values read from a `cha`
    +file into fields within the choreography_sequence_pb2.Animation protobuf
    +message. They are used by the animation_file_to_proto.py file.
    +"""
    +
    +from bosdyn.api.spot import (choreography_sequence_pb2, choreography_service_pb2,
    +                             choreography_service_pb2_grpc)
    +
    +
    +def start_time_handler(val, animation_frame):
    +    animation_frame.time = val
    +    return animation_frame
    +
    +
    +def fl_angles_handler(vals, animation_frame):
    +    animation_frame.legs.fl.joint_angles.hip_x = vals[0]
    +    animation_frame.legs.fl.joint_angles.hip_y = vals[1]
    +    animation_frame.legs.fl.joint_angles.knee = vals[2]
    +    return animation_frame
    +
    +
    +def fr_angles_handler(vals, animation_frame):
    +    animation_frame.legs.fr.joint_angles.hip_x = vals[0]
    +    animation_frame.legs.fr.joint_angles.hip_y = vals[1]
    +    animation_frame.legs.fr.joint_angles.knee = vals[2]
    +    return animation_frame
    +
    +
    +def hl_angles_handler(vals, animation_frame):
    +    animation_frame.legs.hl.joint_angles.hip_x = vals[0]
    +    animation_frame.legs.hl.joint_angles.hip_y = vals[1]
    +    animation_frame.legs.hl.joint_angles.knee = vals[2]
    +    return animation_frame
    +
    +
    +def hr_angles_handler(vals, animation_frame):
    +    animation_frame.legs.hr.joint_angles.hip_x = vals[0]
    +    animation_frame.legs.hr.joint_angles.hip_y = vals[1]
    +    animation_frame.legs.hr.joint_angles.knee = vals[2]
    +    return animation_frame
    +
    +
    +def fl_pos_handler(vals, animation_frame):
    +    animation_frame.legs.fl.foot_pos.x.value = vals[0]
    +    animation_frame.legs.fl.foot_pos.y.value = vals[1]
    +    animation_frame.legs.fl.foot_pos.z.value = vals[2]
    +    return animation_frame
    +
    +
    +def fr_pos_handler(vals, animation_frame):
    +    animation_frame.legs.fr.foot_pos.x.value = vals[0]
    +    animation_frame.legs.fr.foot_pos.y.value = vals[1]
    +    animation_frame.legs.fr.foot_pos.z.value = vals[2]
    +    return animation_frame
    +
    +
    +def hl_pos_handler(vals, animation_frame):
    +    animation_frame.legs.hl.foot_pos.x.value = vals[0]
    +    animation_frame.legs.hl.foot_pos.y.value = vals[1]
    +    animation_frame.legs.hl.foot_pos.z.value = vals[2]
    +    return animation_frame
    +
    +
    +def hr_pos_handler(vals, animation_frame):
    +    animation_frame.legs.hr.foot_pos.x.value = vals[0]
    +    animation_frame.legs.hr.foot_pos.y.value = vals[1]
    +    animation_frame.legs.hr.foot_pos.z.value = vals[2]
    +    return animation_frame
    +
    +
    +def gripper_handler(val, animation_frame):
    +    animation_frame.gripper.gripper_angle.value = val
    +    return animation_frame
    +
    +
    +def fl_contact_handler(val, animation_frame):
    +    animation_frame.legs.fl.stance.value = val
    +    return animation_frame
    +
    +
    +def fr_contact_handler(val, animation_frame):
    +    animation_frame.legs.fr.stance.value = val
    +    return animation_frame
    +
    +
    +def hl_contact_handler(val, animation_frame):
    +    animation_frame.legs.hl.stance.value = val
    +    return animation_frame
    +
    +
    +def hr_contact_handler(val, animation_frame):
    +    animation_frame.legs.hr.stance.value = val
    +    return animation_frame
    +
    +
    +def sh0_handler(val, animation_frame):
    +    animation_frame.arm.joint_angles.shoulder_0.value = val
    +    return animation_frame
    +
    +
    +def sh1_handler(val, animation_frame):
    +    animation_frame.arm.joint_angles.shoulder_1.value = val
    +    return animation_frame
    +
    +
    +def el0_handler(val, animation_frame):
    +    animation_frame.arm.joint_angles.elbow_0.value = val
    +    return animation_frame
    +
    +
    +def el1_handler(val, animation_frame):
    +    animation_frame.arm.joint_angles.elbow_1.value = val
    +    return animation_frame
    +
    +
    +def wr0_handler(val, animation_frame):
    +    animation_frame.arm.joint_angles.wrist_0.value = val
    +    return animation_frame
    +
    +
    +def wr1_handler(val, animation_frame):
    +    animation_frame.arm.joint_angles.wrist_1.value = val
    +    return animation_frame
    +
    +
    +def fl_hx_handler(val, animation_frame):
    +    animation_frame.legs.fl.joint_angles.hip_x = val
    +    return animation_frame
    +
    +
    +def fl_hy_handler(val, animation_frame):
    +    animation_frame.legs.fl.joint_angles.hip_y = val
    +    return animation_frame
    +
    +
    +def fl_kn_handler(val, animation_frame):
    +    animation_frame.legs.fl.joint_angles.knee = val
    +    return animation_frame
    +
    +
    +def fr_hx_handler(val, animation_frame):
    +    animation_frame.legs.fr.joint_angles.hip_x = val
    +    return animation_frame
    +
    +
    +def fr_hy_handler(val, animation_frame):
    +    animation_frame.legs.fr.joint_angles.hip_y = val
    +    return animation_frame
    +
    +
    +def fr_kn_handler(val, animation_frame):
    +    animation_frame.legs.fr.joint_angles.knee = val
    +    return animation_frame
    +
    +
    +def hl_hx_handler(val, animation_frame):
    +    animation_frame.legs.hl.joint_angles.hip_x = val
    +    return animation_frame
    +
    +
    +def hl_hy_handler(val, animation_frame):
    +    animation_frame.legs.hl.joint_angles.hip_y = val
    +    return animation_frame
    +
    +
    +def hl_kn_handler(val, animation_frame):
    +    animation_frame.legs.hl.joint_angles.knee = val
    +    return animation_frame
    +
    +
    +def hr_hx_handler(val, animation_frame):
    +    animation_frame.legs.hr.joint_angles.hip_x = val
    +    return animation_frame
    +
    +
    +def hr_hy_handler(val, animation_frame):
    +    animation_frame.legs.hr.joint_angles.hip_y = val
    +    return animation_frame
    +
    +
    +def hr_kn_handler(val, animation_frame):
    +    animation_frame.legs.hr.joint_angles.knee = val
    +    return animation_frame
    +
    +
    +def fl_x_handler(val, animation_frame):
    +    animation_frame.legs.fl.foot_pos.x.value = val
    +    return animation_frame
    +
    +
    +def fl_y_handler(val, animation_frame):
    +    animation_frame.legs.fl.foot_pos.y.value = val
    +    return animation_frame
    +
    +
    +def fl_z_handler(val, animation_frame):
    +    animation_frame.legs.fl.foot_pos.z.value = val
    +    return animation_frame
    +
    +
    +def fr_x_handler(val, animation_frame):
    +    animation_frame.legs.fr.foot_pos.x.value = val
    +    return animation_frame
    +
    +
    +def fr_y_handler(val, animation_frame):
    +    animation_frame.legs.fr.foot_pos.y.value = val
    +    return animation_frame
    +
    +
    +def fr_z_handler(val, animation_frame):
    +    animation_frame.legs.fr.foot_pos.z.value = val
    +    return animation_frame
    +
    +
    +def hl_x_handler(val, animation_frame):
    +    animation_frame.legs.hl.foot_pos.x.value = val
    +    return animation_frame
    +
    +
    +def hl_y_handler(val, animation_frame):
    +    animation_frame.legs.hl.foot_pos.y.value = val
    +    return animation_frame
    +
    +
    +def hl_z_handler(val, animation_frame):
    +    animation_frame.legs.hl.foot_pos.z.value = val
    +    return animation_frame
    +
    +
    +def hr_x_handler(val, animation_frame):
    +    animation_frame.legs.hr.foot_pos.x.value = val
    +    return animation_frame
    +
    +
    +def hr_y_handler(val, animation_frame):
    +    animation_frame.legs.hr.foot_pos.y.value = val
    +    return animation_frame
    +
    +
    +def hr_z_handler(val, animation_frame):
    +    animation_frame.legs.hr.foot_pos.z.value = val
    +    return animation_frame
    +
    +
    +def body_x_handler(val, animation_frame):
    +    animation_frame.body.body_pos.x.value = val
    +    return animation_frame
    +
    +
    +def body_y_handler(val, animation_frame):
    +    animation_frame.body.body_pos.y.value = val
    +    return animation_frame
    +
    +
    +def body_z_handler(val, animation_frame):
    +    animation_frame.body.body_pos.z.value = val
    +    return animation_frame
    +
    +
    +def com_x_handler(val, animation_frame):
    +    animation_frame.body.com_pos.x.value = val
    +    return animation_frame
    +
    +
    +def com_y_handler(val, animation_frame):
    +    animation_frame.body.com_pos.y.value = val
    +    return animation_frame
    +
    +
    +def com_z_handler(val, animation_frame):
    +    animation_frame.body.com_pos.z.value = val
    +    return animation_frame
    +
    +
    +def body_quat_x_handler(val, animation_frame):
    +    animation_frame.body.quaternion.x = val
    +    return animation_frame
    +
    +
    +def body_quat_y_handler(val, animation_frame):
    +    animation_frame.body.quaternion.y = val
    +    return animation_frame
    +
    +
    +def body_quat_z_handler(val, animation_frame):
    +    animation_frame.body.quaternion.z = val
    +    return animation_frame
    +
    +
    +def body_quat_w_handler(val, animation_frame):
    +    animation_frame.body.quaternion.w = val
    +    return animation_frame
    +
    +
    +def body_roll_handler(val, animation_frame):
    +    animation_frame.body.euler_angles.roll.value = val
    +    return animation_frame
    +
    +
    +def body_pitch_handler(val, animation_frame):
    +    animation_frame.body.euler_angles.pitch.value = val
    +    return animation_frame
    +
    +
    +def body_yaw_handler(val, animation_frame):
    +    animation_frame.body.euler_angles.yaw.value = val
    +    return animation_frame
    +
    +
    +def body_pos_handler(vals, animation_frame):
    +    animation_frame.body.body_pos.x.value = vals[0]
    +    animation_frame.body.body_pos.y.value = vals[1]
    +    animation_frame.body.body_pos.z.value = vals[2]
    +    return animation_frame
    +
    +
    +def com_pos_handler(vals, animation_frame):
    +    animation_frame.body.com_pos.x.value = vals[0]
    +    animation_frame.body.com_pos.y.value = vals[1]
    +    animation_frame.body.com_pos.z.value = vals[2]
    +    return animation_frame
    +
    +
    +def body_euler_rpy_angles_handler(vals, animation_frame):
    +    animation_frame.body.euler_angles.roll.value = vals[0]
    +    animation_frame.body.euler_angles.pitch.value = vals[1]
    +    animation_frame.body.euler_angles.yaw.value = vals[2]
    +    return animation_frame
    +
    +
    +def body_quaternion_xyzw_handler(vals, animation_frame):
    +    animation_frame.body.quaternion.x = vals[0]
    +    animation_frame.body.quaternion.y = vals[1]
    +    animation_frame.body.quaternion.z = vals[2]
    +    animation_frame.body.quaternion.w = vals[3]
    +    return animation_frame
    +
    +
    +def body_quaternion_wxyz_handler(vals, animation_frame):
    +    animation_frame.body.quaternion.x = vals[1]
    +    animation_frame.body.quaternion.y = vals[2]
    +    animation_frame.body.quaternion.z = vals[3]
    +    animation_frame.body.quaternion.w = vals[0]
    +    return animation_frame
    +
    +
    +def leg_angles_handler(vals, animation_frame):
    +    animation_frame.legs.fl.joint_angles.hip_x = vals[0]
    +    animation_frame.legs.fl.joint_angles.hip_y = vals[1]
    +    animation_frame.legs.fl.joint_angles.knee = vals[2]
    +    animation_frame.legs.fr.joint_angles.hip_x = vals[3]
    +    animation_frame.legs.fr.joint_angles.hip_y = vals[4]
    +    animation_frame.legs.fr.joint_angles.knee = vals[5]
    +    animation_frame.legs.hl.joint_angles.hip_x = vals[6]
    +    animation_frame.legs.hl.joint_angles.hip_y = vals[7]
    +    animation_frame.legs.hl.joint_angles.knee = vals[8]
    +    animation_frame.legs.hr.joint_angles.hip_x = vals[9]
    +    animation_frame.legs.hr.joint_angles.hip_y = vals[10]
    +    animation_frame.legs.hr.joint_angles.knee = vals[11]
    +    return animation_frame
    +
    +
    +def foot_pos_handler(vals, animation_frame):
    +    animation_frame.legs.fl.foot_pos.x.value = vals[0]
    +    animation_frame.legs.fl.foot_pos.y.value = vals[1]
    +    animation_frame.legs.fl.foot_pos.z.value = vals[2]
    +    animation_frame.legs.fr.foot_pos.x.value = vals[3]
    +    animation_frame.legs.fr.foot_pos.y.value = vals[4]
    +    animation_frame.legs.fr.foot_pos.z.value = vals[5]
    +    animation_frame.legs.hl.foot_pos.x.value = vals[6]
    +    animation_frame.legs.hl.foot_pos.y.value = vals[7]
    +    animation_frame.legs.hl.foot_pos.z.value = vals[8]
    +    animation_frame.legs.hr.foot_pos.x.value = vals[9]
    +    animation_frame.legs.hr.foot_pos.y.value = vals[10]
    +    animation_frame.legs.hr.foot_pos.z.value = vals[11]
    +    return animation_frame
    +
    +
    +def contact_handler(vals, animation_frame):
    +    animation_frame.legs.fl.stance.value = vals[0]
    +    animation_frame.legs.fr.stance.value = vals[1]
    +    animation_frame.legs.hl.stance.value = vals[2]
    +    animation_frame.legs.hr.stance.value = vals[3]
    +    return animation_frame
    +
    +
    +def arm_joints_handler(vals, animation_frame):
    +    animation_frame.arm.joint_angles.shoulder_0.value = vals[0]
    +    animation_frame.arm.joint_angles.shoulder_1.value = vals[1]
    +    animation_frame.arm.joint_angles.elbow_0.value = vals[2]
    +    animation_frame.arm.joint_angles.elbow_1.value = vals[3]
    +    animation_frame.arm.joint_angles.wrist_0.value = vals[4]
    +    animation_frame.arm.joint_angles.wrist_1.value = vals[5]
    +    return animation_frame
    +
    +
    +def hand_x_handler(vals, animation_frame):
    +    animation_frame.arm.hand_pose.position.x = vals[0]
    +    return animation_frame
    +
    +
    +def hand_y_handler(vals, animation_frame):
    +    animation_frame.arm.hand_pose.position.y = vals[0]
    +    return animation_frame
    +
    +
    +def hand_z_handler(vals, animation_frame):
    +    animation_frame.arm.hand_pose.position.z = vals[0]
    +    return animation_frame
    +
    +
    +def hand_quat_x_handler(val, animation_frame):
    +    animation_frame.arm.hand_pose.quaternion.x = val
    +    return animation_frame
    +
    +
    +def hand_quat_y_handler(val, animation_frame):
    +    animation_frame.arm.hand_pose.quaternion.y = val
    +    return animation_frame
    +
    +
    +def hand_quat_z_handler(val, animation_frame):
    +    animation_frame.arm.hand_pose.quaternion.z = val
    +    return animation_frame
    +
    +
    +def hand_quat_w_handler(val, animation_frame):
    +    animation_frame.arm.hand_pose.quaternion.w = val
    +    return animation_frame
    +
    +
    +def hand_roll_handler(val, animation_frame):
    +    animation_frame.arm.hand_pose.euler_angles.roll.value = val
    +    return animation_frame
    +
    +
    +def hand_pitch_handler(val, animation_frame):
    +    animation_frame.arm.hand_pose.euler_angles.pitch.value = val
    +    return animation_frame
    +
    +
    +def hand_yaw_handler(val, animation_frame):
    +    animation_frame.arm.hand_pose.euler_angles.yaw.value = val
    +    return animation_frame
    +
    +
    +def hand_pos_handler(vals, animation_frame):
    +    animation_frame.arm.hand_pose.position.x.value = vals[0]
    +    animation_frame.arm.hand_pose.position.y.value = vals[1]
    +    animation_frame.arm.hand_pose.position.z.value = vals[2]
    +    return animation_frame
    +
    +
    +def hand_euler_rpy_angles_handler(vals, animation_frame):
    +    animation_frame.arm.hand_pose.euler_angles.roll.value = vals[0]
    +    animation_frame.arm.hand_pose.euler_angles.pitch.value = vals[1]
    +    animation_frame.arm.hand_pose.euler_angles.yaw.value = vals[2]
    +    return animation_frame
    +
    +
    +def hand_quaternion_xyzw_handler(vals, animation_frame):
    +    animation_frame.arm.hand_pose.quaternion.x = vals[0]
    +    animation_frame.arm.hand_pose.quaternion.y = vals[1]
    +    animation_frame.arm.hand_pose.quaternion.z = vals[2]
    +    animation_frame.arm.hand_pose.quaternion.w = vals[3]
    +    return animation_frame
    +
    +
    +def hand_quaternion_wxyz_handler(vals, animation_frame):
    +    animation_frame.arm.hand_pose.quaternion.x = vals[1]
    +    animation_frame.arm.hand_pose.quaternion.y = vals[2]
    +    animation_frame.arm.hand_pose.quaternion.z = vals[3]
    +    animation_frame.arm.hand_pose.quaternion.w = vals[0]
    +    return animation_frame
    +
    +
    +def controls_option(file_line_split, animation):
    +    for track in file_line_split:
    +        if track == "legs":
    +            animation.proto.controls_legs = True
    +        elif track == "arm":
    +            animation.proto.controls_arm = True
    +        elif track == "body":
    +            animation.proto.controls_body = True
    +        elif track == "gripper":
    +            animation.proto.controls_gripper = True
    +        elif track == "controls":
    +            continue
    +        else:
    +            print("Unknown track name %s" % track)
    +    return animation
    +
    +
    +def bpm_option(file_line_split, animation):
    +    bpm = file_line_split[1]
    +    animation.bpm = int(bpm)
    +    return animation
    +
    +
    +def extendable_option(file_line_split, animation):
    +    animation.proto.extendable = True
    +    return animation
    +
    +
    +def truncatable_option(file_line_split, animation):
    +    animation.proto.truncatable = True
    +    return animation
    +
    +
    +def neutral_start_option(file_line_split, animation):
    +    animation.proto.neutral_start = True
    +    return animation
    +
    +
    +def precise_steps_option(file_line_split, animation):
    +    animation.proto.precise_steps = True
    +
    +def precise_timing_option(file_line_split, animation):
    +    animation.proto.precise_timing = True
    +
    +def no_looping_option(file_line_split, animation):
    +    animation.proto.no_looping = True
    +
    +def arm_required_option(file_line_split, animation):
    +    animation.proto.arm_required = True
    +
    +def arm_prohibited_option(file_line_split, animation):
    +    animation.proto.arm_prohibited = True
    +
    +def track_swing_trajectories_option(file_line_split, animation):
    +    animation.proto.track_swing_trajectories = True
    +    return animation
    +
    +def assume_zero_roll_and_pitch_option(file_line_split, animation):
    +    animation.proto.assume_zero_roll_and_pitch = True
    +    return animation
    +
    +def track_hand_rt_body_option(file_line_split, animation):
    +    animation.proto.track_hand_rt_body = True
    +    return animation
    +
    +
    +def track_hand_rt_feet_option(file_line_split, animation):
    +    animation.proto.track_hand_rt_feet = True
    +    return animation
    +
    +
    +def arm_playback_option(file_line_split, animation):
    +    playback = file_line_split[1]
    +    if playback == "jointspace":
    +        animation.proto.arm_playback = choreography_sequence_pb2.Animation.ARM_PLAYBACK_JOINTSPACE
    +    elif playback == "workspace":
    +        animation.proto.arm_playback = choreography_sequence_pb2.Animation.ARM_PLAYBACK_WORKSPACE
    +    elif playback == "workspace_dance_frame":
    +        animation.proto.arm_playback = choreography_sequence_pb2.Animation.ARM_PLAYBACK_WORKSPACE_DANCE_FRAME
    +    else:
    +        animation.proto.arm_playback = choreography_sequence_pb2.Animation.ARM_PLAYBACK_DEFAULT
    +        print("Unknown arm playbak option %s" % playback)
    +    return animation
    +
    +
    +def display_rgb_option(file_line_split, animation):
    +    for i in range(1, 3):
    +        animation.rgb[i - 1] = int(file_line_split[i])
    +    return animation
    +
    +
    +def frequency_option(file_line_split, animation):
    +    freq = file_line_split[1]
    +    animation.frequency = float(freq)
    +    return animation
    +
    +
    +def retime_to_integer_slices_option(file_line_split, animation):
    +    animation.proto.retime_to_integer_slices = True
    +    return animation
    +
    +
    +def description_option(file_line_split, animation):
    +    description = " ".join(file_line_split[1:])
    +    description = description.replace('"', '')  # remove any quotation marks
    +    animation.description = description
    +    return animation
    diff --git a/python/bosdyn-choreography-client/src/bosdyn/choreography/client/animation_file_to_proto.py b/python/bosdyn-choreography-client/src/bosdyn/choreography/client/animation_file_to_proto.py
    new file mode 100644
    index 000000000..d2d372283
    --- /dev/null
    +++ b/python/bosdyn-choreography-client/src/bosdyn/choreography/client/animation_file_to_proto.py
    @@ -0,0 +1,572 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""A tool to convert animation files into protobuf messages which can be uploaded to the robot
    +and used within choreography sequences."""
    +
    +import argparse
    +import hashlib
    +import logging
    +import ntpath
    +import os
    +import sys
    +
    +from google.protobuf import text_format, wrappers_pb2
    +
    +import bosdyn.client
    +import bosdyn.client.util
    +from bosdyn.api.spot import choreography_sequence_pb2
    +from bosdyn.choreography.client.animation_file_conversion_helpers import *
    +
    +LOGGER = logging.getLogger(__name__)
    +
    +# The options keywords represent the first section of the file, and will be parsed into
    +# specific fields within the Animation proto.
    +OPTIONS_KEYWORDS_TO_FUNCTION = {
    +    "controls": controls_option,
    +    "bpm": bpm_option,
    +    "extendable": extendable_option,
    +    "truncatable": truncatable_option,
    +    "neutral_start": neutral_start_option,
    +    "precise_steps": precise_steps_option,
    +    "precise_timing": precise_timing_option,
    +    "no_looping": no_looping_option,
    +    "arm_required": arm_required_option,
    +    "arm_prohibited": arm_prohibited_option,
    +    "track_swing_trajectories": track_swing_trajectories_option,
    +    "assume_zero_roll_and_pitch": assume_zero_roll_and_pitch_option,
    +    "arm_playback": arm_playback_option,
    +    "display_rgb": display_rgb_option,
    +    "frequency": frequency_option,
    +    "retime_to_integer_slices": retime_to_integer_slices_option,
    +    "description": description_option,
    +}
    +
    +# The grouped headers represent animation keyframe values which can be used and specify multiple protobuf
    +# values for a single header keyword. For example, body_pos has x/y/z position values.
    +GROUPED_HEADERS = {
    +    "body_pos": (3, body_pos_handler),
    +    "com_pos": (3, com_pos_handler),
    +    "body_euler_rpy": (3, body_euler_rpy_angles_handler),
    +    "body_quat_xyzw": (4, body_quaternion_xyzw_handler),
    +    "body_quat_wxyz": (4, body_quaternion_wxyz_handler),
    +    "leg_joints": (12, leg_angles_handler),
    +    "foot_pos": (12, foot_pos_handler),
    +    "hand_pos": (3, hand_pos_handler),
    +    "hand_euler_rpy": (3, hand_euler_rpy_angles_handler),
    +    "hand_quat_xyzw": (4, hand_quaternion_xyzw_handler),
    +    "hand_quat_wxyz": (4, hand_quaternion_wxyz_handler),
    +    "contact": (4, contact_handler),
    +    "arm_joints": (6, arm_joints_handler),
    +    "fl_angles": (3, fl_angles_handler),
    +    "fr_angles": (3, fr_angles_handler),
    +    "hl_angles": (3, hl_angles_handler),
    +    "hr_angles": (3, hr_angles_handler),
    +    "fl_pos": (3, fl_pos_handler),
    +    "fr_pos": (3, fr_pos_handler),
    +    "hl_pos": (3, hl_pos_handler),
    +    "hr_pos": (3, hr_pos_handler),
    +}
    +
    +# The single grouped headers represent animation keyframe values which can be used and specify a single
    +# protobuf value for the header keyword.
    +SINGLE_HEADERS = {
    +    "body_x": body_x_handler,
    +    "body_y": body_y_handler,
    +    "body_z": body_z_handler,
    +    "com_x": com_x_handler,
    +    "com_y": com_y_handler,
    +    "com_z": com_z_handler,
    +    "body_quat_w": body_quat_w_handler,
    +    "body_quat_x": body_quat_x_handler,
    +    "body_quat_y": body_quat_y_handler,
    +    "body_quat_z": body_quat_z_handler,
    +    "body_roll": body_roll_handler,
    +    "body_pitch": body_pitch_handler,
    +    "body_yaw": body_yaw_handler,
    +    "fl_hx": fl_hx_handler,
    +    "fl_hy": fl_hy_handler,
    +    "fl_kn": fl_kn_handler,
    +    "fr_hx": fr_hx_handler,
    +    "fr_hy": fr_hy_handler,
    +    "fr_kn": fr_kn_handler,
    +    "hl_hx": hl_hx_handler,
    +    "hl_hy": hl_hy_handler,
    +    "hl_kn": hl_kn_handler,
    +    "hr_hx": hr_hx_handler,
    +    "hr_hy": hr_hy_handler,
    +    "hr_kn": hr_kn_handler,
    +    "fl_x": fl_x_handler,
    +    "fl_y": fl_y_handler,
    +    "fl_z": fl_z_handler,
    +    "fr_x": fr_x_handler,
    +    "fr_y": fr_y_handler,
    +    "fr_z": fr_z_handler,
    +    "hr_x": hr_x_handler,
    +    "hr_y": hr_y_handler,
    +    "hr_z": hr_z_handler,
    +    "hl_x": hl_x_handler,
    +    "hl_y": hl_y_handler,
    +    "hl_z": hl_z_handler,
    +    "fl_contact": fl_contact_handler,
    +    "fr_contact": fr_contact_handler,
    +    "hl_contact": hl_contact_handler,
    +    "hr_contact": hr_contact_handler,
    +    "shoulder0": sh0_handler,
    +    "shoulder1": sh1_handler,
    +    "elbow0": el0_handler,
    +    "elbow1": el1_handler,
    +    "wrist0": wr0_handler,
    +    "wrist1": wr1_handler,
    +    "hand_x": hand_x_handler,
    +    "hand_y": hand_y_handler,
    +    "hand_z": hand_z_handler,
    +    "hand_quat_w": hand_quat_w_handler,
    +    "hand_quat_x": hand_quat_x_handler,
    +    "hand_quat_y": hand_quat_y_handler,
    +    "hand_quat_z": hand_quat_z_handler,
    +    "hand_roll": hand_roll_handler,
    +    "hand_pitch": hand_pitch_handler,
    +    "hand_yaw": hand_yaw_handler,
    +    "gripper": gripper_handler,
    +    "time": start_time_handler,
    +}
    +
    +COMMENT_DELIMITERS = ["//", "#"]
    +
    +
    +class Animation():
    +    """Helper class to track values read from the animation file that are important to choreographer
    +    and necessary when uploading animated moves."""
    +
    +    def __init__(self):
    +        # The name of the animated move.
    +        self.name = None
    +
    +        # [Optional] The BPM which the move will be performed at.
    +        self.bpm = None
    +
    +        # [Optional] The frequency at which the keyframes occur. If not provided in the CHA file, then
    +        # explicit timestamps must be provided for each keyframe.
    +        self.frequency = None
    +
    +        # Default description for the animated move.
    +        self.description = "Animated dance move."
    +
    +        # The protobuf message representing the animation.
    +        self.proto = choreography_sequence_pb2.Animation()
    +
    +        # The color of the animated move's block when loaded in Choreographer.
    +        self.rgb = [100, 100, 100]
    +
    +        # Each individual parameter line as read from a *.cha file.
    +        self.parameter_lines = []
    +
    +    def create_move_info_proto(self):
    +        """Creates the MoveInfo protobuf message from the parsed animation file.
    +
    +        Returns:
    +            The choreography_sequence.MoveInfo protobuf message for the animation as generated
    +            by the different animation fields in the Animation proto.
    +        """
    +        move_info = choreography_sequence_pb2.MoveInfo()
    +        move_info.name = self.name
    +        move_info.is_extendable = self.proto.extendable or self.proto.truncatable  # "is adjustable"
    +
    +        # Should always have move.time, so the duration is the final time of the last frame.
    +        move_duration_sec = self.proto.animation_keyframes[-1].time
    +        if self.bpm is not None:
    +            # Compute the move length slices using the bpm.
    +            slices_per_minute = 4 * self.bpm
    +            move_duration_minutes = move_duration_sec / 60.0
    +            move_info.move_length_slices = int(move_duration_minutes * slices_per_minute)
    +        else:
    +            # Just use the time to size the move.
    +            move_info.move_length_time = move_duration_sec
    +
    +        # Set the max/min info based on truncatable/extendable flags.
    +        if self.proto.truncatable and not self.proto.extendable:
    +            move_info.max_time = move_info.move_length_time
    +            move_info.max_move_length_slices = move_info.move_length_slices
    +        elif self.proto.extendable and not self.proto.truncatable:
    +            move_info.min_time = move_info.move_length_time
    +            move_info.min_move_length_slices = move_info.move_length_slices
    +
    +        # Set the different track information
    +        move_info.controls_arm = self.proto.controls_arm
    +        move_info.controls_gripper = self.proto.controls_gripper
    +        move_info.controls_legs = self.proto.controls_legs
    +        move_info.controls_body = self.proto.controls_body
    +
    +        # Set the choreographer-specific display information
    +        move_info.display.category = choreography_sequence_pb2.ChoreographerDisplayInfo.CATEGORY_ANIMATION
    +        move_info.display.color.r = self.rgb[0]
    +        move_info.display.color.g = self.rgb[1]
    +        move_info.display.color.b = self.rgb[2]
    +        move_info.display.color.a = 1.0
    +        move_info.display.description = self.description
    +
    +        # Animations are required to start and end in a standing position.
    +        move_info.entrance_states.append(choreography_sequence_pb2.MoveInfo.TRANSITION_STATE_STAND)
    +        move_info.exit_state = choreography_sequence_pb2.MoveInfo.TRANSITION_STATE_STAND
    +
    +        return move_info
    +
    +
    +def set_proto(proto_object, attribute_name, attribute_value):
    +    """Helper function to set a field to a specific value in the protobuf message.
    +
    +    Args:
    +        proto_object (Protobuf message): Any generic protobuf message.
    +        attribute_name (String): The field name within the protobuf message.
    +        attribute_value: A value with type matching the field type defined in the protobuf
    +            message definition. This will be saved in the attribute_name field.
    +
    +    Returns:
    +        Nothing. Mutates the input proto_object to update the specified field name to the
    +        provided value.
    +    """
    +    field_attr = getattr(proto_object, attribute_name)
    +    field_type = type(field_attr.value)
    +    field_attr.value = field_type(attribute_value)
    +
    +
    +def handle_nested_double_value_params(proto_object, name, attribute_value):
    +    """Helper function to set a field to a DoubleValue protobuf in a protobuf message.
    +
    +    Args:
    +        proto_object (Protobuf message): Any generic protobuf message.
    +        name (String): The field name within the protobuf message. This name should
    +            be both the field name and sub-field name separated by a period. For example,
    +            for the Vec3 velocity field, the name would be 'velocity.x'.
    +        attribute_value: A value with type matching the field type defined in the protobuf
    +            message definition. This will be saved in the attribute_name field.
    +
    +    Returns:
    +        Nothing. Mutates the input proto_object to update the specified field name to the
    +        provided value.
    +    """
    +    subfields = name.split(".")
    +    current_attr = proto_object
    +    for field in subfields:
    +        current_attr = getattr(current_attr, field)
    +    current_attr.CopyFrom(wrappers_pb2.DoubleValue(value=attribute_value))
    +
    +
    +def read_animation_params(animation):
    +    """Parses the set of lines that are the parameters section of the file.
    +
    +    Reads the parameter lines into the min/max/default values in the Animation proto.
    +
    +    Args:
    +        animation (Animation): The animation class structure containing the parameter lines.
    +
    +    Returns:
    +        The mutated animation class, which now contains populated params fields in the
    +        animation's protobuf.
    +    """
    +    current_params_default = animation.proto.default_parameters
    +    current_params_min = animation.proto.minimum_parameters
    +    current_params_max = animation.proto.maximum_parameters
    +    group_field_splitter = "."
    +    for param in animation.parameter_lines:
    +        split_line = param.split()
    +        param_name = str(split_line[0])
    +        min_max_default_vals = [
    +            float(split_line[i]) if float(split_line[i]) != 0 else 1e-6 for i in range(1, 4)
    +        ]
    +
    +        # Now create the parameter protobuf message.
    +        if group_field_splitter in param_name:
    +            # Non-individual fields, so handle slightly differently.
    +            try:
    +                handle_nested_double_value_params(current_params_min, param_name,
    +                                                  min_max_default_vals[0])
    +                handle_nested_double_value_params(current_params_default, param_name,
    +                                                  min_max_default_vals[1])
    +                handle_nested_double_value_params(current_params_max, param_name,
    +                                                  min_max_default_vals[2])
    +            except AttributeError:
    +                err = "Cannot parse file %s: unknown parameter field name %s." % (animation.name,
    +                                                                                  param_name)
    +                raise Exception(err)
    +        else:
    +            # Individual field using a DoubleValue proto.
    +            try:
    +                set_proto(current_params_min, param_name, min_max_default_vals[0])
    +                set_proto(current_params_default, param_name, min_max_default_vals[1])
    +                set_proto(current_params_max, param_name, min_max_default_vals[2])
    +            except AttributeError:
    +                err = "Cannot parse file %s: unknown parameter field name %s." % (animation.name,
    +                                                                                  param_name)
    +                raise Exception(err)
    +    return animation
    +
    +
    +def read_and_find_animation_params(animate_move_params_file, filepath_input=True):
    +    """Create a mapping of the parameter name to the default parameter values.
    +
    +    Args:
    +        animate_move_params_file (string): filepath to the default parameters file, or a string representing the contents
    +            of the parameters file.
    +        filepath_input (boolean): With filepath_input set to True, the animate_move_params_file argument will be interpreted as
    +            a file path to the default parameters file. When set to False, the animate_move_params_file argument will be read as
    +            the information in the default parameters file passed as a string.
    +
    +    Returns:
    +        A dictionary containing the parameter name as the key, and the full parameter line from
    +        the file as the value.
    +    """
    +    if(filepath_input):
    +        #if animate_move_params_file is a filepath open the file
    +        params_file = open(animate_move_params_file, "r")
    +    else:
    +        #if animate_move_params_file is a string of parameter information split the string by newline characters
    +        params_file = animate_move_params_file.splitlines()
    +
    +    reading_animate_params = False
    +    animate_params_vals = {}
    +    for line in params_file:
    +        line = line.strip()
    +        if "animate_params" in line:
    +            # Starting the animate params section.
    +            reading_animate_params = True
    +            continue
    +
    +        if reading_animate_params and line == "":
    +            # Finished reading the animate_params section.
    +            reading_animate_params = False
    +            return animate_params_vals
    +
    +        if reading_animate_params:
    +            # Set the parameter name as the key, and the full parameter line (as a string) as the value in
    +            # the animate_params_vals dictionary.
    +            split_line = line.split()
    +            param_name = split_line[0]
    +            animate_params_vals[param_name] = line
    +            continue
    +    return animate_params_vals
    +
    +
    +def convert_animation_file_to_proto(animated_file, animate_move_params_file=""):
    +    """Parses a file into the animation proto that will be uploaded to the robot.
    +
    +    Args:
    +        animated_file (string): The filepath to the animation text file.
    +        animate_move_params_file (string): [Optional] The filepath to a default set of move parameters.
    +
    +    Returns:
    +        The Animation class, which contains the animation proto to be uploaded to the robot, as well
    +        as additional information to be used by Choreographer.
    +    """
    +
    +    # Create a mapping of the default values for each parameter from the MoveParamsConfig.txt file.
    +    # These will be used if a user doesn't provide min/max/default, but does include the parameter
    +    # name in the file.
    +    maybe_use_default_params = animate_move_params_file != ""
    +    default_animate_params_values = {}
    +    if maybe_use_default_params:
    +        if os.path.exists(animate_move_params_file):
    +            # if there is a file at the location animate_move_params_file try to read the file
    +            default_animate_params_values = read_and_find_animation_params(animate_move_params_file)
    +        else:
    +            # if animate_move_params_file isn't a locatable file try to read the string as parameter field data
    +            default_animate_params_values = read_and_find_animation_params(animate_move_params_file, False)
    +
    +    animation = Animation()
    +    animation.name = ntpath.basename(animated_file).split(".cha")[0]
    +
    +    animation_specs = open(animated_file, "r")
    +
    +    # Expecting three chunks, separated by a blank line.
    +    section_counter = 0
    +
    +    # The set of keywords that describe each column in the movement section.
    +    movement_columns = []
    +
    +    # If the keyframe needs the timestamps set based off the frequency, track that information here.
    +    # Value 1: indicates if it needs the timestamps set, value 2: indicates the current cumulative time
    +    # summed while iterating over each keyframe in the file.
    +    set_keyframe_times = [False, 0]
    +
    +    # Make up a unique color that is persistent based on the animation name.
    +    name_hash = hashlib.md5(animation.name.encode())
    +    animation.rgb[0] = int(name_hash.hexdigest()[0:2], 16)
    +    animation.rgb[1] = int(name_hash.hexdigest()[2:4], 16)
    +    animation.rgb[2] = int(name_hash.hexdigest()[4:6], 16)
    +
    +    for line in animation_specs:
    +        line = line.strip()
    +
    +        if line == "":
    +            # The sections are separated by an empty line
    +            section_counter += 1
    +            continue
    +
    +        # Check if there are any comments. Comments can be both at the end of an exisiting line, or
    +        # on a line of there own. They are marked with # or //. We want to just ignore them and continue
    +        # parsing the file as normal.
    +        for delim in COMMENT_DELIMITERS:
    +            line = line.split(delim)[0]  # Take any content before the comment starts.
    +
    +        if line == "":
    +            # If after stripping all the comment content there is no line left, then continue.
    +            # We do NOT increment the section counter for comment lines!
    +            continue
    +
    +        if section_counter == 0:
    +            # the first section is the "options section".
    +            # Take first word of line and use that as the options keyword. Apply whatever function
    +            # is specified for that keyword to the remaining line values.
    +            file_line_split = line.split()
    +            keyword = file_line_split[0]
    +            if keyword in OPTIONS_KEYWORDS_TO_FUNCTION:
    +                # Apply the keywords functionality to the animation class.
    +                OPTIONS_KEYWORDS_TO_FUNCTION[keyword](file_line_split, animation)
    +
    +        elif section_counter == 1:
    +            # Second section represents the parameters for choreographer. Simply store the lines
    +            # for use by choreographer's config reader.
    +            line = line.strip().lower()
    +            if "no parameters" in line:
    +                # This is the keyword for no parameters, so just skip the section and move on.
    +                continue
    +            split_line = line.split()
    +            if len(split_line) == 1 and maybe_use_default_params:
    +                # If the parameter name was provided with no user-specified min/max/default values,
    +                # then attempt to use the default values from MoveParamsConfig.txt.
    +                if split_line[0] in default_animate_params_values:
    +                    animation.parameter_lines.append(default_animate_params_values[split_line[0]])
    +                else:
    +                    err = "Cannot parse file %s: parameter field name %s was provided but is not a default parameter." % (
    +                        animation.name, split_line[0])
    +                    raise Exception(err)
    +            else:
    +                animation.parameter_lines.append(line)
    +
    +        elif section_counter == 2:
    +            # The final section is the animated moves section.
    +            if len(movement_columns) == 0:
    +                # The first line will contain all the different column headers.
    +                movement_columns = line.split()
    +
    +                # If "time" is not in the column, then we should set that for every keyframe based on frequency
    +                if "time" not in movement_columns:
    +                    set_keyframe_times[0] = True
    +                    if animation.frequency is None:
    +                        prefix = "Cannot parse file %s: " % animation.name
    +                        err = prefix + "Either frequency or keyframe timestamps must be provided. Neither were found."
    +                        raise Exception(err)
    +                continue
    +
    +            vals = line.split()
    +            animation_keyframe = choreography_sequence_pb2.AnimationKeyframe()
    +            current_index = 0
    +            for header in movement_columns:
    +                if header in GROUPED_HEADERS:
    +                    # For grouped headers, get the next N line values, where N is specified by the grouped
    +                    # headers dict, and set those in the animation keyframe protobuf message.
    +                    header_activities = GROUPED_HEADERS[header]
    +                    header_values = vals[current_index:current_index +
    +                                         header_activities[0]]  # not inclusive
    +                    header_values = [float(val) if val != '0' else 1e-6 for val in header_values]
    +                    header_activities[1](header_values, animation_keyframe)
    +                    current_index = current_index + header_activities[0]
    +                elif header in SINGLE_HEADERS:
    +                    # Add the single value into the animation keyframe protobuf message.
    +                    header_value = float(vals[current_index])
    +                    if header_value == 0:
    +                        header_value = 1e-6
    +                    SINGLE_HEADERS[header](header_value, animation_keyframe)
    +                    current_index += 1
    +                else:
    +                    # Don't fail silently and mismatch indices of other groups if one group header is not found.
    +                    err = "Cannot parse file %s: Unknown body movement keyword %s" % (
    +                        animation.name, header)
    +                    raise Exception(err)
    +
    +            if set_keyframe_times[0]:
    +                # Update the timestamp in the keyframe, then increment it based on the frequency
    +                animation_keyframe.time = set_keyframe_times[1]
    +                set_keyframe_times[1] += (1.0 / animation.frequency)
    +
    +            # Add the animation frame into the animation proto.
    +            animation.proto.animation_keyframes.extend([animation_keyframe])
    +
    +        else:
    +            # An animation file should only have 3 sections: the options, the parameters, and the body movement keyframes.
    +            err = (
    +                "Cannot parse file %s: Animation file contains more than 3 sections deliniated by whitespace."
    +                " Make sure all comments are included in a exisiting section." % (animation.name))
    +            raise Exception(err)
    +
    +    animation.proto.name = animation.name
    +    if animation.bpm is not None:
    +        animation.proto.bpm = animation.bpm
    +
    +    # Read out the parameters into protobuf messages.
    +    read_animation_params(animation)
    +
    +    return animation
    +
    +
    +def write_animation_to_dest(animation, destination):
    +    """Write the new animation proto to a .cap file.
    +
    +    Args:
    +        animation(Animation class): The animation class object generated by the
    +            `cha` file conversion helpers to save the protobuf from.
    +        destination (string): The full filepath to the location to save the animation
    +            protobuf message.
    +    """
    +    if (animation.name is None):
    +        LOGGER.error("Invalid file name, cannot save choreography sequence.")
    +        raise IOError("Invalid file name, cannot save choreography sequence.")
    +
    +    if not os.path.exists(destination):
    +        LOGGER.error("Path(%s) to save file does not exist. Creating it." % destination)
    +        os.makedirs(destination, exist_ok=True)
    +
    +    animation_proto_bytes = text_format.MessageToString(animation.proto)
    +    with open(str(os.path.join(destination, animation.name + ".cap")), 'w') as f:
    +        f.write(animation_proto_bytes)
    +
    +
    +def main():
    +    parser = argparse.ArgumentParser()
    +    parser.add_argument('--cha-filepath', help='The filename of the animation file.')
    +    parser.add_argument('--cha-directory', help="The filepath to a directory with animation files.")
    +    parser.add_argument(
    +        '--config-filepath', help=
    +        'The filepath for a move params config file. This can be found using the ListAllMoves RPC.',
    +        default="")
    +    parser.add_argument('--destination-filepath',
    +                        help='The file location to save the animation proto.', default=".")
    +    options = parser.parse_args()
    +
    +    if options.cha_filepath:
    +        animation = convert_animation_file_to_proto(options.cha_filepath, options.config_filepath)
    +        write_animation_to_dest(animation, options.destination_filepath)
    +
    +    elif options.cha_directory:
    +        files_in_dir = os.listdir(options.cha_directory)
    +        for filename in files_in_dir:
    +            if filename.endswith(".cha"):
    +                animation = convert_animation_file_to_proto(
    +                    os.path.join(options.cha_directory, filename), options.config_filepath)
    +                write_animation_to_dest(animation, options.destination_filepath)
    +
    +    else:
    +        print(
    +            "Please provide either the --cha-filepath argument for a single animation file, or the --cha-directory argument"
    +            " for a full directory of animation files.")
    +
    +    return True
    +
    +
    +if __name__ == '__main__':
    +    if not main():
    +        sys.exit(1)
    diff --git a/python/bosdyn-choreography-client/src/bosdyn/choreography/client/choreography.py b/python/bosdyn-choreography-client/src/bosdyn/choreography/client/choreography.py
    index b60f596e7..9fa380acd 100644
    --- a/python/bosdyn-choreography-client/src/bosdyn/choreography/client/choreography.py
    +++ b/python/bosdyn-choreography-client/src/bosdyn/choreography/client/choreography.py
    @@ -5,30 +5,35 @@
     # Development Kit License (20191101-BDSDK-SL).
     
     """For clients to use the choreography service"""
    +import collections
     import logging
     import os
    -import collections
    +import hashlib
    +
    +from google.protobuf import text_format
     
    -from bosdyn.client.common import BaseClient
    -from bosdyn.client.common import (error_factory, error_pair, common_header_errors, handle_common_header_errors, common_lease_errors,
    +from bosdyn.api.spot import (choreography_sequence_pb2, choreography_service_pb2,
    +                             choreography_service_pb2_grpc)
    +from bosdyn.client.common import (BaseClient, common_header_errors, common_lease_errors,
    +                                  error_factory, error_pair, handle_common_header_errors,
                                       handle_lease_use_result_errors, handle_unset_status_error)
     from bosdyn.client.exceptions import ResponseError, UnsetStatusError
    -from bosdyn.client.robot_command import NoTimeSyncError, _TimeConverter
    -from bosdyn.api.spot import choreography_sequence_pb2, choreography_service_pb2, choreography_service_pb2_grpc
    -
     from bosdyn.client.lease import add_lease_wallet_processors
    -
    -from google.protobuf import text_format
    +from bosdyn.client.robot_command import NoTimeSyncError, _TimeConverter
    +from bosdyn.util import seconds_to_duration
    +from google.protobuf.wrappers_pb2 import StringValue
     
     LOGGER = logging.getLogger('__name__')
     
    +
     class ChoreographyClient(BaseClient):
         """Client for Choreography Service."""
         default_service_name = 'choreography'
         service_type = 'bosdyn.api.spot.ChoreographyService'
     
         def __init__(self):
    -        super(ChoreographyClient, self).__init__(choreography_service_pb2_grpc.ChoreographyServiceStub)
    +        super(ChoreographyClient,
    +              self).__init__(choreography_service_pb2_grpc.ChoreographyServiceStub)
             self._timesync_endpoint = None
     
         def update_from(self, other):
    @@ -51,54 +56,398 @@ def timesync_endpoint(self):
         def list_all_moves(self, **kwargs):
             """Get a list of the different choreography sequence moves and associated parameters."""
             req = choreography_sequence_pb2.ListAllMovesRequest()
    -        return self.call(self._stub.ListAllMoves, req,
    -                         value_from_response=None, # Return the complete response message
    -                         error_from_response=common_header_errors, **kwargs)
    +        return self.call(
    +            self._stub.ListAllMoves,
    +            req,
    +            value_from_response=None,  # Return the complete response message
    +            error_from_response=common_header_errors,
    +            **kwargs)
     
         def list_all_moves_async(self, object_type=None, time_start_point=None, **kwargs):
             """Async version of list_all_moves()."""
             req = choreography_sequence_pb2.ListAllMovesRequest()
    -        return self.call_async(self._stub.ListAllMoves, req,
    -                               value_from_response=None, # Return the complete response message
    -                               error_from_response=common_header_errors, **kwargs)
    +        return self.call_async(
    +            self._stub.ListAllMoves,
    +            req,
    +            value_from_response=None,  # Return the complete response message
    +            error_from_response=common_header_errors,
    +            **kwargs)
     
         def upload_choreography(self, choreography_seq, non_strict_parsing=True, **kwargs):
    -        """Upload the choreography sequence to the robot."""
    -        req = choreography_sequence_pb2.UploadChoreographyRequest(choreography_sequence=choreography_seq, non_strict_parsing=non_strict_parsing)
    -        return self.call(self._stub.UploadChoreography, req,
    -                        value_from_response=None,  # Return the complete response message
    -                        error_from_response=common_header_errors,
    -                        **kwargs)
    +        """Upload the choreography sequence to the robot.
    +
    +        Args:
    +            choreography_seq (choreography_sequence_pb2.ChoreographySequence proto): The
    +                dance sequence to be sent and stored on the robot.
    +            non_strict_parsing (boolean): If true, the robot will fix any correctable errors within
    +                the choreogrpahy and allow users to run the dance. If false, if there are errors
    +                the robot will reject the choreography when attempting to run the dance.
    +
    +        Returns:
    +            The UploadChoreographyResponse message, which includes any warnings generated from the
    +            validation process for the choreography. If non_strict_parsing=False and there are warnings,
    +            then the dance will not be able to run.
    +        """
    +        req = choreography_sequence_pb2.UploadChoreographyRequest(
    +            choreography_sequence=choreography_seq, non_strict_parsing=non_strict_parsing)
    +        return self.call(
    +            self._stub.UploadChoreography,
    +            req,
    +            value_from_response=None,  # Return the complete response message
    +            error_from_response=common_header_errors,
    +            **kwargs)
     
         def upload_choreography_async(self, choreography_seq, non_strict_parsing=True, **kwargs):
             """Async version of upload_choreography()."""
    -        req = choreography_sequence_pb2.UploadChoreographyRequest(choreography_sequence=choreography_seq, non_strict_parsing=non_strict_parsing)
    -        return self.call_async(self._stub.UploadChoreography, req,
    -                               value_from_response=None, # Return the complete response message
    -                               error_from_response=common_header_errors, **kwargs)
    +        req = choreography_sequence_pb2.UploadChoreographyRequest(
    +            choreography_sequence=choreography_seq, non_strict_parsing=non_strict_parsing)
    +        return self.call_async(
    +            self._stub.UploadChoreography,
    +            req,
    +            value_from_response=None,  # Return the complete response message
    +            error_from_response=common_header_errors,
    +            **kwargs)
    +
    +    def upload_animated_move(self, animation, generated_id="", **kwargs):
    +        """Upload the animation proto to the robot to be used as a move in choreography sequences.
    +
    +        Args:
    +            animation (choreography_sequence_pb2.Animation): The animated move protobuf message. This
    +                can be generated by converting a `cha` file using the animation_file_to_proto helpers.
    +            generated_id (string): The ID hash generated for the animation based on the serialization
    +                of the protobuf message. This can be left empty, and the robot will re-parse and
    +                validate the message. This will be filled out automatically when using
    +                the AnimationUploadHelper.
    +
    +        Returns:
    +            The UploadAnimatedMoveResponse message, which includes warnings if the uploaded animation
    +            was invalid.
    +        """
    +        gen_id_proto = StringValue(value=generated_id)
    +        req = choreography_sequence_pb2.UploadAnimatedMoveRequest(
    +            animated_move=animation, animated_move_generated_id=gen_id_proto)
    +        return self.call(
    +            self._stub.UploadAnimatedMove,
    +            req,
    +            value_from_response=None,  # Return the complete response message
    +            error_from_response=_upload_animated_move_errors,
    +            **kwargs)
    +
    +    def upload_animated_move_async(self, animation, generated_id="", **kwargs):
    +        """Async version of upload_animated_move()."""
    +        gen_id_proto = StringValue(value=generated_id)
    +        req = choreography_sequence_pb2.UploadAnimatedMoveRequest(
    +            animated_move=animation, animated_move_generated_id=gen_id_proto)
    +        return self.call_async(
    +            self._stub.UploadAnimatedMove,
    +            req,
    +            value_from_response=None,  # Return the complete response message
    +            error_from_response=_upload_animated_move_errors,
    +            **kwargs)
    +
    +
    +    def choreography_log_to_animation_file(self, name, fpath, has_arm, *args):
    +        """Turn the choreography log from the proto into an animation `cha` file type.
    +
    +        Args:
    +            name (string): Name that the `cha` file will be saved as.
    +            fpath (string): Location where the new `cha` file will be saved.
    +            has_arm (boolean): True if the robot has an arm, False if the robot doesn't have an arm. When False arm motion won't
    +                    be added to the `cha` file.
    +            *args (string(s) or list): String(s), list of strings, or a mix of string(s) and list(s) that are the options to be
    +                    included in the `cha` file. (ex. 'truncatable')
    +
    +        Returns:
    +            The filename of the new animation `cha` file for the most recent choreography log.
    +        """
    +
    +        def option_list(*argvs):
    +            """Takes a list of options or a variable number of string arguments, or a mix of the two and returns a single list
    +                containing all of the options to be included
    +            """
    +            x=[]
    +            for i in argvs:
    +                if 'list' in str(type(i)):
    +                    x+=list(i)
    +                elif 'str' in str(type(i)):
    +                    x.append(i)
    +            return x
    +
    +        def list_to_formated_string(alist, space):
    +            """Helper function for formatting the `cha` file columns."""
    +            format_str = ''.join(space for _ in alist)
    +            return format_str.format(*alist)
    +
    +        def timestamp_to_seconds(timestamp):
    +            """Helper function to turn a seconds quantity and nanoseconds quantity into one value of unit seconds."""
    +            return timestamp.seconds + 1e-9*timestamp.nanos
    +
    +
    +        #get the choreography log for the recording
    +        log_type = choreography_sequence_pb2.DownloadRobotStateLogRequest.LOG_TYPE_MANUAL
    +        response_status, choreography_log = self.download_robot_state_log(log_type)
    +
    +        #create the header for the *.cha file
    +        joint_type_list = ["leg_joints", "body_pos", "body_quat_xyzw", "time", "contact"]
    +        controls_header = "controls legs body"
    +        description = "Animation created from log recording."
    +
    +        if has_arm:
    +            #if the robot has an arm, add the gripper and arm to the header description and keywords section
    +            joint_type_list.append("arm_joints")
    +            joint_type_list.append("gripper")
    +            controls_header = controls_header + " arm gripper"
    +
    +        #format the complete header with all the options to be included in the *.cha file
    +        joint_spacer = "{:<60}"
    +        header = (controls_header + "\n"
    +                    "description: " + description + "\n")
    +
    +        for option in option_list(*args):
    +            header = header + option + "\n"
    +
    +        header = header + ("\n"
    +                            "no parameters\n\n" + list_to_formated_string(joint_type_list, joint_spacer) + "\n")
    +
    +        spacer_val = '{:<30}'
    +        ext=".cha"
    +        file_path = os.path.join(fpath, name + ext)
    +        initial_time = -1
    +
    +        with open(file_path, 'w') as f:
    +            #write the formatted header to the *.cha file
    +            f.write(header)
    +
    +            #create an empty list to hold the values for the current keyframe
    +            list_values=[]
    +
    +            # get the list of all the keyframes from the recording,
    +            # (the complete description of the robot in space at each timestamp)
    +            keyframe_list = choreography_log.key_frames
    +
    +            for k in keyframe_list:
    +                # for each animation keyframe in the protobuf log put the desired joint values and timestamp in a list
    +                # and then write them to the *.cha file. Values are added to list_values in the same order as the
    +                # joint categories in joint_type_list, and the order of the joint angles within those groups must also
    +                # go in the correct order for the *.cha to be read correctly.
    +                list_values=[]
    +
    +                #leg_joints values:
    +                #front right leg joint values
    +                list_values.append(k.joint_angles.fr.hip_x)
    +                list_values.append(k.joint_angles.fr.hip_y)
    +                list_values.append(k.joint_angles.fr.knee)
    +
    +                #front left leg joint values
    +                list_values.append(k.joint_angles.fl.hip_x)
    +                list_values.append(k.joint_angles.fl.hip_y)
    +                list_values.append(k.joint_angles.fl.knee)
    +
    +                #hind right leg joint values
    +                list_values.append(k.joint_angles.hr.hip_x)
    +                list_values.append(k.joint_angles.hr.hip_y)
    +                list_values.append(k.joint_angles.hr.knee)
    +
    +                #hind left leg joint values
    +                list_values.append(k.joint_angles.hl.hip_x)
    +                list_values.append(k.joint_angles.hl.hip_y)
    +                list_values.append(k.joint_angles.hl.knee)
    +
    +                #body_pos values:
    +                #position of the body in the animation frame
    +                list_values.append(k.animation_tform_body.position.x)
    +                list_values.append(k.animation_tform_body.position.y)
    +                list_values.append(k.animation_tform_body.position.z)
    +
    +                #body_quat_xyzw values:
    +                #rotation of the body in the animation frame
    +                list_values.append(k.animation_tform_body.rotation.x)
    +                list_values.append(k.animation_tform_body.rotation.y)
    +                list_values.append(k.animation_tform_body.rotation.z)
    +                list_values.append(k.animation_tform_body.rotation.w)
    +
    +                #time value:
    +                #time, in seconds, when the keyframe position occurs relative to the start of the recording
    +                time = timestamp_to_seconds(k.timestamp)
    +
    +                #if the initial time is negative it's the first timestamp recorded, set that as the initial time
    +                if(initial_time < 0):
    +                    initial_time = time
    +
    +                #adjust the timestamp so it's relative to the start of the recording
    +                time = time - initial_time
    +                list_values.append(time)
    +
    +                #contact values:
    +                #contact state of each foot; 0 for airborne, 1 for contact with the floor.
    +                list_values.append(k.foot_contact_state.fr_contact)
    +                list_values.append(k.foot_contact_state.fl_contact)
    +                list_values.append(k.foot_contact_state.hr_contact)
    +                list_values.append(k.foot_contact_state.hl_contact)
    +
    +                if has_arm:
    +                    #if the robot has an arm, record the joint values of the arm and gripper
    +
    +                    #arm_joints values:
    +                    list_values.append(k.joint_angles.arm.shoulder_0.value)
    +                    list_values.append(k.joint_angles.arm.shoulder_1.value)
    +                    list_values.append(k.joint_angles.arm.elbow_0.value)
    +                    list_values.append(k.joint_angles.arm.elbow_1.value)
    +                    list_values.append(k.joint_angles.arm.wrist_0.value)
    +                    list_values.append(k.joint_angles.arm.wrist_1.value)
    +
    +                    #gripper value:
    +                    list_values.append(k.joint_angles.gripper_angle.value)
    +
    +                #format the values of the keyframe and write them to the *.cha animation file
    +                f.write(list_to_formated_string(list_values, spacer_val))
    +                f.write("\n")
    +
    +        print("Animation *.cha file downloaded to: %s" % file_path)
    +        return name + ".cha"
    +
    +
    +    def execute_choreography(self, choreography_name, client_start_time,
    +                             choreography_starting_slice, lease=None, **kwargs):
    +        """Execute the current choreography sequence loaded on the robot by name.
    +
    +        Args:
    +            choreography_name (string): The name of the uploaded choreography to run. The robot
    +                only stores a single choreography at a time, so this name should match the last
    +                uploaded choreography.
    +            client_start_time (float): The time (in seconds) that the dance should start. This time
    +                should be provided in the local clock's timeframe and the client will convert it
    +                to the required robot's clock timeframe.
    +            choreography_starting_slice (int): Which slice to start the dance at when the start
    +                time is reached. By default, it will start with the first slice.
    +            lease (lease_pb2.Lease protobuf): A specific lease to use for the request. If nothing is
    +                provided, the client will append the next lease sequence in this field by default.
    +
    +        Returns:
    +            The full ExecuteChoreographyResponse message.
    +        """
    +        req = self.build_execute_choreography_request(choreography_name, client_start_time,
    +                                                      choreography_starting_slice, lease)
    +
    +        return self.call(
    +            self._stub.ExecuteChoreography,
    +            req,
    +            value_from_response=None,  # Return the complete response message
    +            error_from_response=_execute_choreography_errors,
    +            **kwargs)
    +
    +    def execute_choreography_async(self, choreography_name, client_start_time,
    +                                   choreography_starting_slice, lease=None, **kwargs):
    +        """Async version of execute_choreography()."""
    +        req = self.build_execute_choreography_request(choreography_name, client_start_time,
    +                                                      choreography_starting_slice, lease)
    +        return self.call_async(
    +            self._stub.ExecuteChoreography,
    +            req,
    +            value_from_response=None,  # Return the complete response message
    +            error_from_response=_execute_choreography_errors,
    +            **kwargs)
    +
    +    def start_recording_state(self, duration_secs, continue_session_id=0, **kwargs):
    +        """Start (or continue) a manually recorded robot state log.
    +
    +        Args:
    +            duration_secs (float): The duration of the recording request in seconds. This will
    +                apply from when the StartRecording rpc is recieved.
    +            continue_session_id (int): If provided, the RPC will continue the recording
    +                session associated with that ID.
    +
    +        Returns:
    +            The full StartRecordingStateResponse proto.
    +        """
    +        request = self.build_start_recording_state_request(duration_secs, continue_session_id)
    +        return self.call(
    +            self._stub.StartRecordingState,
    +            request,
    +            value_from_response=None,  # Return the complete response message
    +            error_from_response=_start_recording_state_errors,
    +            **kwargs)
    +
    +    def start_recording_state_async(self, duration_secs, continue_session_id=0, **kwargs):
    +        """Async version of start_recording_state()."""
    +        request = self.build_start_recording_state_request(duration_secs, continue_session_id)
    +        return self.call_async(
    +            self._stub.StartRecordingState,
    +            request,
    +            value_from_response=None,  # Return the complete response message
    +            error_from_response=_start_recording_state_errors,
    +            **kwargs)
    +
    +    def stop_recording_state(self, **kwargs):
    +        """Stop recording a manual choreography log.
    +
    +        Returns:
    +            The full StopRecordingStateResponse proto.
    +        """
    +        request = choreography_sequence_pb2.StopRecordingStateRequest()
    +        return self.call(
    +            self._stub.StopRecordingState,
    +            request,
    +            value_from_response=None,  # Return the complete response message
    +            error_from_response=common_header_errors,
    +            **kwargs)
    +
    +    def stop_recording_state_async(self, **kwargs):
    +        """Async version of stop_recording_state()."""
    +        request = choreography_sequence_pb2.StopRecordingStateRequest()
    +        return self.call_async(
    +            self._stub.StopRecordingState,
    +            request,
    +            value_from_response=None,  # Return the complete response message
    +            error_from_response=common_header_errors,
    +            **kwargs)
    +
    +    @staticmethod
    +    def build_start_recording_state_request(duration_seconds=None, continue_session_id=0):
    +        """Generate a StartRecordingStateRequest proto.
    +
    +        Args:
    +            duration_seconds (float): The duration of the recording request in seconds. This will
    +                apply from when the StartRecording rpc is recieved.
    +            continue_session_id (int): If provided, the RPC will continue the recording
    +                session associated with that ID.
    +
    +        Returns:
    +            The full StartRecordingStateRequest proto with fields populated based on the input arguments.
    +        """
    +        request = choreography_sequence_pb2.StartRecordingStateRequest()
    +        request.recording_session_id = continue_session_id
    +        if duration_seconds is not None:
    +            request.continue_recording_duration.CopyFrom(seconds_to_duration(duration_seconds))
    +        return request
     
    -    def execute_choreography(self, choreography_name, client_start_time, choreography_starting_slice, lease=None, **kwargs):
    -        """Execute the current choreography sequence loaded on the robot by name."""
    -        req = self.build_execute_choreography_request(choreography_name, client_start_time, choreography_starting_slice, lease)
     
    -        return self.call(self._stub.ExecuteChoreography, req,
    -                        value_from_response=None,  # Return the complete response message
    -                        error_from_response=_execute_choreography_errors,
    -                        **kwargs)
    +    def download_robot_state_log(self, log_type, **kwargs):
    +        """Download the manual or automatically collected logs for choreography robot state.
     
    -    def execute_choreography_async(self, choreography_name, client_start_time, choreography_starting_slice, lease=None, **kwargs):
    -        """Async version of execute_choreography()."""
    -        req = self.build_execute_choreography_request(choreography_name, client_start_time, choreography_starting_slice, lease)
    -        return self.call_async(self._stub.ExecuteChoreography, req,
    -                               value_from_response=None, # Return the complete response message
    -                               error_from_response=_execute_choreography_errors, **kwargs)
    +        Args:
    +            log_type(choreography_sequence_pb2.LogType): Type of log, either manual or the automatically
    +                generated log for the latest choreography.
     
    -    def build_execute_choreography_request(self, choreography_name, client_start_time, choreography_starting_slice, lease=None):
    +        Returns:
    +            A tuple containing the response status (choreography_sequence_pb2.DownloadRobotStateLogResponse.Status) and
    +            the choreography_sequence_pb2.ChoreographyStateLog constructed from the streaming response message.
    +        """
    +        request = choreography_sequence_pb2.DownloadRobotStateLogRequest(log_type=log_type)
    +        return self.call(
    +            self._stub.DownloadRobotStateLog,
    +            request,
    +            value_from_response=_get_streamed_choreography_state_log,  # Parses streamed response
    +            error_from_response=_download_robot_state_log_stream_errors,
    +            **kwargs)
    +
    +    def build_execute_choreography_request(self, choreography_name, client_start_time,
    +                                           choreography_starting_slice, lease=None):
             """Generate the ExecuteChoreographyRequest rpc with the timestamp converted into robot time."""
             # Note the client_start_time is a time expressed in the client's clock for when the choreography sequence should begin.
    -        request = choreography_sequence_pb2.ExecuteChoreographyRequest(choreography_sequence_name=choreography_name,
    -                                              choreography_starting_slice=float(choreography_starting_slice),
    -                                              lease=lease)
    +        request = choreography_sequence_pb2.ExecuteChoreographyRequest(
    +            choreography_sequence_name=choreography_name,
    +            choreography_starting_slice=float(choreography_starting_slice), lease=lease)
             if client_start_time is not None:
                 request.start_time.CopyFrom(
                     self._update_timestamp_filter(client_start_time, self.timesync_endpoint))
    @@ -113,39 +462,245 @@ def _update_timestamp_filter(self, timestamp, timesync_endpoint):
             return converter.robot_timestamp_from_local_secs(timestamp)
     
     
    +class AnimationUploadHelper:
    +    """Helper class to reduce reuploading animations to a robot multiple times.
    +
    +    This class will generate a hash (unique ID built from the animation protobuf
    +    message's contents) for each animation proto, and include this hash when initially
    +    uploading animations. It will track the animations sent to the robot and the hashes, and
    +    only sends RPCs to upload an animation when the incoming animation proto is different
    +    from the one on robot.
    +
    +    It initializes the set of known animations on robot already by using the ListAllMoves
    +    RPC and reading the existing animation names and hashes.
    +
    +    The hash function is generated using a library which guarantees consistency, even when
    +    restarting the program. As well, the hash is built from the serialized protobuf, and
    +    proto guarantees that within the language that the serialized message will be consistent.
    +
    +    Args:
    +        robot (Robot sdk instance): The robot to upload animations to.
    +    """
    +
    +    ANIMATION_MOVE_PREFIX = "animation::"
    +
    +    def __init__(self, robot):
    +        self.robot = robot
    +        self.choreography_client = robot.ensure_client(ChoreographyClient.default_service_name)
    +
    +        # Track animation name and current hash on robot.
    +        self.animation_name_to_generated_id = {}
    +
    +        # Initialize the list of known animations and their hashes based on the robot's
    +        # ListAllMoves RPC response.
    +        self.initialize()
    +
    +    def initialize(self):
    +        """Determine which animations are already uploaded on robot."""
    +        # Get a list of all the exisiting animations on robot.
    +        initial_move_list = self.choreography_client.list_all_moves()
    +
    +        # Iterate over the list of moves the robot currently has. Track any animation moves
    +        # by name and current animation hash. Any moves uploaded using this helper class will
    +        # save the animation's hash in this dictionary and compare new animation hashes to
    +        # determine
    +        for move in initial_move_list.moves:
    +            if AnimationUploadHelper.ANIMATION_MOVE_PREFIX in move.name:
    +                # Use the move name without the prefix so that subsequent attempts to upload
    +                # that move will still match correctly.
    +                move_name = move.name.split(AnimationUploadHelper.ANIMATION_MOVE_PREFIX)[1]
    +                gen_id = move.animated_move_generated_id.value
    +                self.animation_name_to_generated_id[move_name] = gen_id
    +
    +
    +    def upload_animated_move(self, animation, **kwargs):
    +        """Uploads the animation to robot if the animation protobuf has changed.
    +
    +        This will only send an UploadAnimatedMove RPC if the incoming animation
    +        has a new hash that differs from the current hash for this animation on robot, which
    +        indicates that the animation protobuf has changed since the last one uploaded to robot.
    +
    +        Args:
    +            animation_proto(choreography_sequence_pb2.Animation): Animation to maybe upload.
    +
    +        Returns:
    +            The UploadAnimateMoveResponse protobuf message if the animation is actually sent.
    +            If the animation protobuf has not changed and is not sent to the robot, then this
    +            function returns None.
    +        """
    +        generated_id = self.generate_animation_id(animation)
    +        if animation.name in self.animation_name_to_generated_id:
    +            gen_id_on_robot = self.animation_name_to_generated_id[animation.name]
    +            if gen_id_on_robot == generated_id:
    +                # Exit early without uploading the animation since it already exists on robot.
    +                return None
    +        result = self.choreography_client.upload_animated_move(animation, generated_id, **kwargs)
    +        if result.status == choreography_sequence_pb2.UploadAnimatedMoveResponse.STATUS_OK:
    +            # Add the move name to the tracked list.
    +            self.animation_name_to_generated_id[animation.name] = generated_id
    +        return result
    +
    +
    +    def generate_animation_id(self, animation_proto):
    +        """Serialize an Animation protobuf message and create a hash from the binary string.
    +
    +        NOTE: Protobuf's serialization will not be consistent across protobuf versions or
    +        even just different languages serializing the same protobuf message. This means that for a
    +        single protobuf message, there could be multiple different serializations. This is ok for the
    +        use-case of the AnimationUploadHelper since the id's are only used for a specific
    +        "session" of Choreographer and the robot's boot session. These are not meant to be the same
    +        for forever and ever due to the potential inconsistencies mentioned, and should not be used
    +        with that expectation.
    +
    +        Further, if a single animation proto does not generate the same ID for one "session", then
    +        it will just be reuploaded and processed by the robot again.
    +
    +        Args:
    +            animation_proto(choreography_sequence_pb2.Animation): Animation to generate a hash for.
    +
    +        Returns:
    +            A string representing a unique hash built from the animation proto.
    +        """
    +        return hashlib.sha1(animation_proto.SerializeToString()).hexdigest()
    +
    +
     class InvalidUploadedChoreographyError(ResponseError):
         """The uploaded choreography is invalid and unable to be performed."""
     
    +
     class RobotCommandIssuesError(ResponseError):
         """A problem occurred when issuing the robot command containing the dance."""
     
    +
     class LeaseError(ResponseError):
         """Incorrect or invalid leases for data acquisition. Check the lease use results."""
     
    +
    +class AnimationValidationFailedError(ResponseError):
    +    """The uploaded animation file is invalid and cannot be used in choreography sequences."""
    +
    +
    +class NoRecordedInformation(ResponseError):
    +    """The choreography service has no logged robot state data."""
    +
    +
    +class UnknownRecordingSessionId(ResponseError):
    +    """The recording request contains an unknown recording session ID."""
    +
    +
    +class RecordingBufferFull(ResponseError):
    +    """The recording buffer is full and the current manual log will be truncated."""
    +
    +
    +class IncompleteData(ResponseError):
    +    """The recording buffer filled up, the returned log will be truncated."""
    +
    +
     _EXECUTE_CHOREOGRAPHY_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
     _EXECUTE_CHOREOGRAPHY_STATUS_TO_ERROR.update({
         choreography_sequence_pb2.ExecuteChoreographyResponse.STATUS_OK: (None, None),
    -    choreography_sequence_pb2.ExecuteChoreographyResponse.STATUS_INVALID_UPLOADED_CHOREOGRAPHY: (InvalidUploadedChoreographyError,
    -                                                             InvalidUploadedChoreographyError.__doc__),
    +    choreography_sequence_pb2.ExecuteChoreographyResponse.STATUS_INVALID_UPLOADED_CHOREOGRAPHY:
    +        (InvalidUploadedChoreographyError, InvalidUploadedChoreographyError.__doc__),
         choreography_sequence_pb2.ExecuteChoreographyResponse.STATUS_ROBOT_COMMAND_ISSUES:
    -    (RobotCommandIssuesError, RobotCommandIssuesError.__doc__),
    +        (RobotCommandIssuesError, RobotCommandIssuesError.__doc__),
         choreography_sequence_pb2.ExecuteChoreographyResponse.STATUS_LEASE_ERROR:
             (LeaseError, LeaseError.__doc__),
     })
     
    +
     @handle_common_header_errors
     @handle_lease_use_result_errors
     @handle_unset_status_error(unset='STATUS_UNKNOWN')
     def _execute_choreography_errors(response):
         """Return an exception based on response from ExecuteChoreography RPC, None if no error."""
    -    return error_factory(response, response.status,
    -                         status_to_string=choreography_sequence_pb2.ExecuteChoreographyResponse.Status.Name,
    -                         status_to_error=_EXECUTE_CHOREOGRAPHY_STATUS_TO_ERROR)
    +    return error_factory(
    +        response, response.status,
    +        status_to_string=choreography_sequence_pb2.ExecuteChoreographyResponse.Status.Name,
    +        status_to_error=_EXECUTE_CHOREOGRAPHY_STATUS_TO_ERROR)
    +
    +
    +_UPLOAD_ANIMATED_MOVE_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
    +_UPLOAD_ANIMATED_MOVE_STATUS_TO_ERROR.update({
    +    choreography_sequence_pb2.UploadAnimatedMoveResponse.STATUS_OK: (None, None),
    +    choreography_sequence_pb2.UploadAnimatedMoveResponse.STATUS_ANIMATION_VALIDATION_FAILED:
    +        (AnimationValidationFailedError, AnimationValidationFailedError.__doc__),
    +})
    +
    +
    +@handle_common_header_errors
    +@handle_unset_status_error(unset='STATUS_UNKNOWN')
    +def _upload_animated_move_errors(response):
    +    """Return an exception based on response from UploadAnimatedMove RPC, None if no error."""
    +    return error_factory(
    +        response, response.status,
    +        status_to_string=choreography_sequence_pb2.UploadAnimatedMoveResponse.Status.Name,
    +        status_to_error=_UPLOAD_ANIMATED_MOVE_STATUS_TO_ERROR)
    +
    +
    +_START_RECORDING_STATE_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
    +_START_RECORDING_STATE_STATUS_TO_ERROR.update({
    +    choreography_sequence_pb2.StartRecordingStateResponse.STATUS_OK: (None, None),
    +    choreography_sequence_pb2.StartRecordingStateResponse.STATUS_UNKNOWN_RECORDING_SESSION_ID:
    +        (UnknownRecordingSessionId, UnknownRecordingSessionId.__doc__),
    +    choreography_sequence_pb2.StartRecordingStateResponse.STATUS_RECORDING_BUFFER_FULL:
    +        (RecordingBufferFull, RecordingBufferFull.__doc__),
    +})
    +
    +
    +@handle_common_header_errors
    +@handle_unset_status_error(unset='STATUS_UNKNOWN')
    +def _start_recording_state_errors(response):
    +    """Return an exception based on response from StartRecordingState RPC, None if no error."""
    +    return error_factory(
    +        response, response.status,
    +        status_to_string=choreography_sequence_pb2.StartRecordingStateResponse.Status.Name,
    +        status_to_error=_START_RECORDING_STATE_STATUS_TO_ERROR)
    +
    +
    +@handle_common_header_errors
    +@handle_unset_status_error(unset='STATUS_UNKNOWN')
    +def _download_robot_state_log_stream_errors(response):
    +    """Return a custom exception based on download robot state log streaming response, None if no error."""
    +    # Iterate through the response since the download request responds with a stream.
    +    for resp in response:
    +        # Handle error statuses from the request.
    +        if (resp.status == choreography_sequence_pb2.DownloadRobotStateLogResponse.
    +                STATUS_NO_RECORDED_INFORMATION):
    +            return NoRecordedInformation(response=resp, error_message=NoRecordedInformation.__doc__)
    +    # All responses (in the iterator) had status_ok
    +    return None
    +
     
     '''
     Static helper methods.
     '''
     
    +def _get_streamed_choreography_state_log(response):
    +    """Reads a streamed response to recreate a ChoreographyStateLog proto.
    +
    +    Args:
    +        response(choreography_sequence_pb2.DownloadRobotStateLogResponse): Streamed response with the
    +            choreography log.
    +
    +    Returns:
    +        A tuple containing the response status (choreography_sequence_pb2.DownloadRobotStateLogResponse.Status) and
    +        the choreography_sequence_pb2.ChoreographyStateLog constructed from the streaming response message.
    +    """
    +    data = ''
    +    num_chunks = 0
    +    initial_status = None
    +    for resp in response:
    +        if num_chunks == 0:
    +            initial_status = resp.status
    +            data = resp.chunk.data
    +        else:
    +            data += resp.chunk.data
    +        num_chunks += 1
    +    choreography_log = choreography_sequence_pb2.ChoreographyStateLog()
    +    if (num_chunks > 0):
    +        choreography_log.ParseFromString(data)
    +    return (initial_status, choreography_log)
    +
     def load_choreography_sequence_from_binary_file(file_path):
         """Read a choreography sequence file into a protobuf ChoreographySequence message."""
         if not os.path.exists(file_path):
    @@ -159,6 +714,7 @@ def load_choreography_sequence_from_binary_file(file_path):
     
         return choreography_sequence
     
    +
     def load_choreography_sequence_from_txt_file(file_path):
         if not os.path.exists(file_path):
             LOGGER.error("File not found at: %s" % file_path)
    @@ -171,6 +727,7 @@ def load_choreography_sequence_from_txt_file(file_path):
     
         return choreography_sequence
     
    +
     def save_choreography_sequence_to_file(file_path, file_name, choreography):
         """Saves a choreography sequence to a file."""
         if (file_name is None or len(file_name) == 0):
    diff --git a/python/bosdyn-client/setup.py b/python/bosdyn-client/setup.py
    index d0a0dc1cd..dc753bf69 100644
    --- a/python/bosdyn-client/setup.py
    +++ b/python/bosdyn-client/setup.py
    @@ -36,7 +36,7 @@
         package_data={'': ['*.pem']},
         install_requires=[
             'bosdyn-api=={}'.format(SDK_VERSION), 'bosdyn-core=={}'.format(SDK_VERSION), 'grpcio',
    -        'pyjwt', 'numpy', 'requests', 'Deprecated~=1.2.10'
    +        'pyjwt', 'numpy', 'requests', 'Deprecated~=1.2.10',
         ],
         classifiers=[
             "Programming Language :: Python :: 3.6",
    diff --git a/python/bosdyn-client/src/bosdyn/client/README.md b/python/bosdyn-client/src/bosdyn/client/README.md
    index 3359e8467..238692ab4 100644
    --- a/python/bosdyn-client/src/bosdyn/client/README.md
    +++ b/python/bosdyn-client/src/bosdyn/client/README.md
    @@ -15,6 +15,7 @@ Client code and interfaces for the Boston Dynamics robot API.
     * [Arm Surface Contact](arm_surface_contact)
     * [Async Tasks](async_tasks)
     * [Auth](auth)
    +* [Auto Return](auto_return)
     * [BDDF](bddf)
     * [BDDF Download](bddf_download)
     * [Channel](channel)
    @@ -38,12 +39,14 @@ Client code and interfaces for the Boston Dynamics robot API.
     * [Graph Nav](graph_nav)
     * [Image](image)
     * [Image Service Helpers](image_service_helpers)
    +* [IR Enable/Disable](ir_enable_disable)
     * [Lease](lease)
     * [License](license)
     * [Local Grid](local_grid)
     * [Log Annotation](log_annotation)
     * [Math Helpers](math_helpers)
     * [Manipulation API](manipulation_api_client)
    +* [Map Processing](map_processing)
     * [Network Compute Bridge](network_compute_bridge_client)
     * [Payload Registration](payload_registration)
     * [Payload](payload)
    @@ -74,26 +77,37 @@ The table below specifies the protobuf service definitions supported by each cli
     |:------:|:-------------:|
     | [**Arm Surface Contact**](./arm_surface_contact.py) | [arm_surface_contact_service.proto](../../../../../protos/bosdyn/api/arm_surface_contact_service.proto) |
     | [**Auth**](./auth.py) | [auth_service.proto](../../../../../protos/bosdyn/api/auth_service.proto) |
    -| [**DirectoryRegistration**](./directory_registration.py) | [directory_registration_service.proto](../../../../../protos/bosdyn/api/directory_registration_service.proto) |
    +| [**Auto Return**](./auto_return.py) | [auto_return_service.proto](../../../../../protos/bosdyn/api/auto_return/auto_return_service.proto) |
    +| [**Data**](./data_service.py) | [data_service.proto](../../../../../protos/bosdyn/api/data_service.proto) |
    +| [**Data Acquisition**](./data_acquisition.py) | [data_acquisition_service.proto](../../../../../protos/bosdyn/api/data_acquisition_service.proto) |
    +| [**Data Acquisition Plugin**](./data_acquisition_plugin.py) | [data_acquisition_plugin_service.proto](../../../../../protos/bosdyn/api/data_acquisition_plugin_service.proto) |
    +| [**Data Acquisition Store**](./data_acquisition_store.py) | [data_acquisition_store_service.proto](../../../../../protos/bosdyn/api/data_acquisition_store_service.proto) |
    +| [**Data Buffer**](./data_buffer.py) | [data_buffer_service.proto](../../../../../protos/bosdyn/api/data_buffer_service.proto) |
    +| [**Directory Registration**](./directory_registration.py) | [directory_registration_service.proto](../../../../../protos/bosdyn/api/directory_registration_service.proto) |
     | [**Directory**](./directory.py) | [directory_service.proto](../../../../../protos/bosdyn/api/directory_service.proto) |
     | [**Docking**](./docking.py) | [docking/docking_service.proto](../../../../../protos/bosdyn/api/docking/docking_service.proto) |
     | [**Door**](./door.py) | [door_service.proto](../../../../../protos/bosdyn/api/spot/door_service.proto) |
     | [**Estop**](./estop.py) | [estop_service.proto](../../../../../protos/bosdyn/api/estop_service.proto) |
    +| [**Fault**](./fault.py) | [fault_service.proto](../../../../../protos/bosdyn/api/fault_service.proto) |
     | [**GraphNav**](./graph_nav.py) | [graph_nav/graph_nav_service.proto](../../../../../protos/bosdyn/api/graph_nav/graph_nav_service.proto) |
     | [**Image**](./image.py) | [image_service.proto](../../../../../protos/bosdyn/api/image_service.proto) |
    +| [**IR Enable/Disable**](./ir_enable_disable.py) | [ir_enable_disable_service.proto](../../../../../protos/bosdyn/api/ir_enable_disable_service.proto) |
     | [**Lease**](./lease.py) | [lease_service.proto](../../../../../protos/bosdyn/api/lease_service.proto) |
    -| [**LocalGrid**](./local_grid.py) | [local_grid_service.proto](../../../../../protos/bosdyn/api/local_grid_service.proto) |
    -| [**LogAnnotation**](./log_annotation.py) | [log_annotation_service.proto](../../../../../protos/bosdyn/api/log_annotation_service.proto) |
    +| [**License**](./license.py) | [license_service.proto](../../../../../protos/bosdyn/api/license_service.proto) |
    +| [**Local Grid**](./local_grid.py) | [local_grid_service.proto](../../../../../protos/bosdyn/api/local_grid_service.proto) |
    +| [**Log Annotation**](./log_annotation.py) | [log_annotation_service.proto](../../../../../protos/bosdyn/api/log_annotation_service.proto) |
     | [**Manipulation API**](./manipulation_api_client.py) | [manipulation_api_service.proto](../../../../../protos/bosdyn/api/manipulation_api_service.proto) |
    +| [**Map Processing**](./map_processing.py) | [map_processing_service.proto](../../../../../protos/bosdyn/api/graph_nav/map_processing_service.proto) |
     | [**Network Compute Bridge**](./network_compute_bridge_client.py) | [network_compute_bridge_service.proto](../../../../../protos/bosdyn/api/network_compute_bridge_service.proto) |
    -| [**PayloadRegistration**](./payload_registration.py) | [payload_registration_service.proto](../../../../../protos/bosdyn/api/payload_registration_service.proto) |
    +| [**Payload Registration**](./payload_registration.py) | [payload_registration_service.proto](../../../../../protos/bosdyn/api/payload_registration_service.proto) |
     | [**Payload**](./payload.py) | [payload_service.proto](../../../../../protos/bosdyn/api/payload_service.proto) |
    +| [**Point Cloud**](./point_cloud.py) | [point_cloud_service.proto](../../../../../protos/bosdyn/api/point_cloud_service.proto) |
     | [**Power**](./power.py) | [power_service.proto](../../../../../protos/bosdyn/api/power_service.proto) |
     | [**Recording**](./recording.py) | [graph_nav/recording_service.proto](../../../../../protos/bosdyn/api/graph_nav/recording_service.proto) |
    -| [**RobotCommand**](./robot_command.py) | [robot_command_service.proto](../../../../../protos/bosdyn/api/robot_command_service.proto) |
    -| [**RobotId**](./robot_id.py) | [robot_id_service.proto](../../../../../protos/bosdyn/api/robot_id_service.proto) |
    -| [**RobotState**](./robot_state.py) | [robot_state_service.proto](../../../../../protos/bosdyn/api/robot_state_service.proto) |
    +| [**Robot Command**](./robot_command.py) | [robot_command_service.proto](../../../../../protos/bosdyn/api/robot_command_service.proto) |
    +| [**Robot Id**](./robot_id.py) | [robot_id_service.proto](../../../../../protos/bosdyn/api/robot_id_service.proto) |
    +| [**Robot State**](./robot_state.py) | [robot_state_service.proto](../../../../../protos/bosdyn/api/robot_state_service.proto) |
     | [**SpotCam**](./spot_cam/README.py) | [spot_cam/service.proto](../../../../../protos/bosdyn/api/spot_cam/service.proto) |
    -| [**SpotCheck**](./spot_check.py) | [spot/spot_check_service.proto](../../../../../protos/bosdyn/api/spot/spot_check_service.proto) |
    -| [**TimeSync**](./time_sync.py) | [time_sync_service.proto](../../../../../protos/bosdyn/api/time_sync_service.proto) |
    -| [**WorldObject**](./world_object.py) | [world_object_service.proto](../../../../../protos/bosdyn/api/world_object_service.proto) |
    +| [**Spot Check**](./spot_check.py) | [spot/spot_check_service.proto](../../../../../protos/bosdyn/api/spot/spot_check_service.proto) |
    +| [**Time Sync**](./time_sync.py) | [time_sync_service.proto](../../../../../protos/bosdyn/api/time_sync_service.proto) |
    +| [**World Object**](./world_object.py) | [world_object_service.proto](../../../../../protos/bosdyn/api/world_object_service.proto) |
    diff --git a/python/bosdyn-client/src/bosdyn/client/__init__.py b/python/bosdyn-client/src/bosdyn/client/__init__.py
    index e804fe575..633817051 100644
    --- a/python/bosdyn-client/src/bosdyn/client/__init__.py
    +++ b/python/bosdyn-client/src/bosdyn/client/__init__.py
    @@ -18,6 +18,8 @@
                                     InternalServerError,
                                     UnsetStatusError,
                                     RpcError,
    +                                RetryableRpcError,
    +                                PersistentRpcError,
                                     ClientCancelledOperationError,
                                     InvalidAppTokenError,
                                     InvalidClientCertificateError,
    diff --git a/python/bosdyn-client/src/bosdyn/client/arm_surface_contact.py b/python/bosdyn-client/src/bosdyn/client/arm_surface_contact.py
    index 625fceb55..af374640a 100644
    --- a/python/bosdyn-client/src/bosdyn/client/arm_surface_contact.py
    +++ b/python/bosdyn-client/src/bosdyn/client/arm_surface_contact.py
    @@ -12,6 +12,7 @@
     from .lease import add_lease_wallet_processors
     from bosdyn.client.robot_command import NoTimeSyncError, _TimeConverter, _edit_proto
     
    +
     class ArmSurfaceContactClient(BaseClient):
         """Client for the ArmSurfaceContact service."""
         default_service_name = 'arm-surface-contact'
    @@ -76,6 +77,7 @@ def arm_surface_contact_command_async(self, request, **kwargs):
             self._update_command_timestamps(request)
             return self.call_async(self._stub.ArmSurfaceContact, request, **kwargs)
     
    +
     # Tree of proto fields leading to Timestamp protos which need to be converted from
     # client clock to robot clock values using timesync information from the robot.
     # Note, the "@" sign indicates a oneof field. The "None" indicates the field which
    @@ -85,10 +87,10 @@ def arm_surface_contact_command_async(self, request, **kwargs):
             'pose_trajectory_in_task': {
                 'reference_time': None
             },
    -        'gripper_command':{
    +        'gripper_command': {
                 'trajectory': {
                     'reference_time': None
                 }
             }
         }
    -}
    \ No newline at end of file
    +}
    diff --git a/python/bosdyn-client/src/bosdyn/client/auth.py b/python/bosdyn-client/src/bosdyn/client/auth.py
    index 2e90f2948..2b3cae276 100644
    --- a/python/bosdyn-client/src/bosdyn/client/auth.py
    +++ b/python/bosdyn-client/src/bosdyn/client/auth.py
    @@ -51,16 +51,16 @@ class InvalidApplicationTokenError(AuthResponseError):
     _STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
     _STATUS_TO_ERROR.update({
         auth_pb2.GetAuthTokenResponse.STATUS_OK: (None, None),
    -    auth_pb2.GetAuthTokenResponse.STATUS_INVALID_LOGIN: (InvalidLoginError,
    -                                                         InvalidLoginError.__doc__),
    -    auth_pb2.GetAuthTokenResponse.STATUS_INVALID_TOKEN: (InvalidTokenError,
    -                                                         InvalidTokenError.__doc__),
    +    auth_pb2.GetAuthTokenResponse.STATUS_INVALID_LOGIN:
    +        (InvalidLoginError, InvalidLoginError.__doc__),
    +    auth_pb2.GetAuthTokenResponse.STATUS_INVALID_TOKEN:
    +        (InvalidTokenError, InvalidTokenError.__doc__),
         auth_pb2.GetAuthTokenResponse.STATUS_TEMPORARILY_LOCKED_OUT:
    -    (TemporarilyLockedOutError, TemporarilyLockedOutError.__doc__),
    +        (TemporarilyLockedOutError, TemporarilyLockedOutError.__doc__),
         auth_pb2.GetAuthTokenResponse.STATUS_INVALID_APPLICATION_TOKEN:
    -    (InvalidApplicationTokenError, InvalidApplicationTokenError.__doc__),
    +        (InvalidApplicationTokenError, InvalidApplicationTokenError.__doc__),
         auth_pb2.GetAuthTokenResponse.STATUS_EXPIRED_APPLICATION_TOKEN:
    -    (ExpiredApplicationTokenError, ExpiredApplicationTokenError.__doc__)
    +        (ExpiredApplicationTokenError, ExpiredApplicationTokenError.__doc__)
     })
     
     
    diff --git a/python/bosdyn-client/src/bosdyn/client/auto_return.py b/python/bosdyn-client/src/bosdyn/client/auto_return.py
    new file mode 100644
    index 000000000..51cf5a774
    --- /dev/null
    +++ b/python/bosdyn-client/src/bosdyn/client/auto_return.py
    @@ -0,0 +1,112 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""Client implementation of the AutoReturn service."""
    +
    +from __future__ import print_function
    +
    +import collections
    +
    +from bosdyn.client.exceptions import ResponseError
    +from bosdyn.client.common import (error_factory, handle_common_header_errors,
    +                                  handle_unset_status_error, error_pair, BaseClient)
    +from bosdyn.api.auto_return import auto_return_pb2
    +from bosdyn.api.auto_return import auto_return_service_pb2_grpc
    +
    +
    +class AutoReturnResponseError(ResponseError):
    +    """Error in Auto Return RPC"""
    +
    +
    +class InvalidParameterError(AutoReturnResponseError):
    +    """One or more parameters were invalid."""
    +
    +
    +class AutoReturnClient(BaseClient):
    +    """A client for configuring automatic AutoReturn behavior."""
    +
    +    default_service_name = 'auto-return'
    +    service_type = 'bosdyn.api.auto_return.AutoReturnService'
    +
    +    def __init__(self):
    +        super(AutoReturnClient, self).__init__(auto_return_service_pb2_grpc.AutoReturnServiceStub)
    +        self._timesync_endpoint = None
    +
    +    def configure(self, params, leases, **kwargs):
    +        """Set the configuration of the AutoReturn system.
    +
    +        Args:
    +          params (bosdyn.api.auto_return.auto_return_pb2.Params): Parameters to use.
    +          leases (list of bosdyn.client.Lease)
    +
    +        Raises:
    +          AutoReturnResponseError: An invalid request was received by the service.
    +          RpcError: Problem communicating with the service.
    +
    +        Returns:
    +            The bosdyn.api.auto_return_pb2.ConfigureResponse.
    +        """
    +
    +        request = self._configure_request(params, leases)
    +        return self.call(self._stub.Configure, request, None, configure_error, **kwargs)
    +
    +    def configure_async(self, params, leases, **kwargs):
    +        """Async version of the configure() RPC."""
    +        request = self._configure_request(params, leases)
    +        return self.call(self._stub.Configure, request, None, configure_error, **kwargs)
    +
    +    def get_configuration(self, **kwargs):
    +        """Get the configuration of the AutoReturn system.
    +
    +        Raises:
    +          RpcError: Problem communicating with the service.
    +
    +        Returns:
    +            The bosdyn.api.auto_return_pb2.GetConfigurationResponse.
    +        """
    +        request = auto_return_pb2.GetConfigurationRequest()
    +        return self.call(self._stub.GetConfiguration, request, None, None, **kwargs)
    +
    +    def get_configuration_async(self, **kwargs):
    +        """Async version of the get_configuration() RPC."""
    +        request = auto_return_pb2.GetConfigurationRequest()
    +        return self.call_async(self._stub.GetConfiguration, request, None, None, **kwargs)
    +
    +    def start(self, **kwargs):
    +        """Start AutoReturn now.
    +        Raises:
    +          RpcError: Problem communicating with the service.
    +
    +        Returns:
    +            The bosdyn.api.auto_return_pb2.GetConfigurationResponse.
    +        """
    +        request = auto_return_pb2.StartRequest()
    +        return self.call(self._stub.Start, request, None, None, **kwargs)
    +
    +    def start_async(self, **kwargs):
    +        """Async version of the start() RPC."""
    +        request = auto_return_pb2.StartRequest()
    +        return self.call_async(self._stub.Start, request, None, None, **kwargs)
    +
    +    def _configure_request(self, params, leases):
    +        request = auto_return_pb2.ConfigureRequest(params=params)
    +        for lease in leases:
    +            request.leases.add().CopyFrom(lease.lease_proto)
    +        return request
    +
    +
    +_CONFIGURE_STATUS_TO_ERROR = collections.defaultdict(lambda: (None, None))
    +_CONFIGURE_STATUS_TO_ERROR.update(
    +    {auto_return_pb2.ConfigureResponse.STATUS_INVALID_PARAMS: error_pair(InvalidParameterError)})
    +
    +
    +@handle_common_header_errors
    +@handle_unset_status_error(unset='STATUS_UNKNOWN')
    +def configure_error(response):
    +    """Return a custom exception based on the Configure response, None if no error."""
    +    return error_factory(response, response.status,
    +                         status_to_string=auto_return_pb2.ConfigureResponse.Status.Name,
    +                         status_to_error=_CONFIGURE_STATUS_TO_ERROR)
    diff --git a/python/bosdyn-client/src/bosdyn/client/bddf_download.py b/python/bosdyn-client/src/bosdyn/client/bddf_download.py
    index c50ebf861..2f3d782bc 100644
    --- a/python/bosdyn-client/src/bosdyn/client/bddf_download.py
    +++ b/python/bosdyn-client/src/bosdyn/client/bddf_download.py
    @@ -7,10 +7,11 @@
     """Code for downloading robot data in bddf format."""
     import logging
     import re
    +import ssl
     import sys
     
    -import requests
    -from urllib3.exceptions import InsecureRequestWarning
    +from urllib.request import Request, urlopen
    +from urllib.parse import urlencode
     
     from bosdyn.client.time_sync import (TimeSyncEndpoint, TimeSyncClient, NotEstablishedError,
                                          robot_time_range_from_nanoseconds, timespec_to_robot_timespan)
    @@ -19,6 +20,7 @@
     LOGGER = logging.getLogger()
     
     REQUEST_CHUNK_SIZE = 10 * (1024**2)  # This value is not guaranteed.
    +REQUEST_TIMEOUT = 20  # Seconds.
     
     DEFAULT_OUTPUT = "./download.bddf"
     
    @@ -83,11 +85,6 @@ def download_data(  # pylint: disable=too-many-arguments,too-many-locals
         Returns:
           output filename, or None on error
         """
    -    # pylint: disable=no-member
    -    # Suppress only the single warning from urllib3 needed.
    -    requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
    -    # pylint: enable=no-member
    -
         time_sync_endpoint = None
         if not robot_time:
             # Establish time sync with robot to obtain skew.
    @@ -113,16 +110,20 @@ def download_data(  # pylint: disable=too-many-arguments,too-many-locals
             get_params['grpc_service'] = grpc_service
     
         # Request the data.
    -    url = _bddf_url(hostname)
    -    with requests.get(url, headers=_http_headers(robot), verify=False, stream=True,
    -                      params=get_params) as resp:
    -        if resp.status_code != 200:
    -            LOGGER.error("%s %s response: %d", url, get_params, resp.status_code)
    +    url = _bddf_url(hostname) + '?{}'.format(urlencode(get_params))
    +    request = Request(url, headers=_http_headers(robot))
    +    context = ssl._create_unverified_context()  # pylint: disable=protected-access
    +    with urlopen(request, context=context, timeout=REQUEST_TIMEOUT) as resp:
    +        if resp.status != 200:
    +            LOGGER.error("%s %s response: %d", url, get_params, resp.status)
                 return None
     
             outfile = output_filename if output_filename else _output_filename(resp)
             with open(outfile, 'wb') as fid:
    -            for chunk in resp.iter_content(chunk_size=REQUEST_CHUNK_SIZE):
    +            while True:
    +                chunk = resp.read(REQUEST_CHUNK_SIZE)
    +                if len(chunk) == 0:
    +                    break
                     if show_progress:
                         print('.', end='', flush=True)
                     fid.write(chunk)
    @@ -165,8 +166,8 @@ def main():
         add_common_arguments(parser)
         options = parser.parse_args()
     
    -    if options.verbose:
    -        LOGGER.setLevel(logging.DEBUG)
    +    options.verbose = level = logging.DEBUG if options.verbose else logging.INFO
    +    logging.basicConfig(level=level, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
     
         if options.help_timespan:
             _print_help_timespan()
    @@ -184,7 +185,7 @@ def main():
             LOGGER.error("Cannot authenticate to robot to obtain token: %s", err)
             return 1
     
    -    output_filename = download_data(robot, options.hostname, options.timespan,
    +    output_filename = download_data(robot, options.hostname, timespan_spec=options.timespan,
                                         robot_time=options.robot_time, channel=options.channel,
                                         message_type=options.type, grpc_service=options.service,
                                         show_progress=True)
    diff --git a/python/bosdyn-client/src/bosdyn/client/channel.py b/python/bosdyn-client/src/bosdyn/client/channel.py
    index c68abcd31..56434ef53 100644
    --- a/python/bosdyn-client/src/bosdyn/client/channel.py
    +++ b/python/bosdyn-client/src/bosdyn/client/channel.py
    @@ -10,10 +10,10 @@
     
     from .exceptions import (
         RpcError, ClientCancelledOperationError, InvalidAppTokenError, InvalidClientCertificateError,
    -    RetryableUnavailableError, NonexistentAuthorityError, NotFoundError, PermissionDeniedError,
    -    ProxyConnectionError, ResponseTooLargeError, ServiceFailedDuringExecutionError,
    +    NonexistentAuthorityError, NotFoundError, PermissionDeniedError, ProxyConnectionError,
    +    RetryableUnavailableError, ResponseTooLargeError, ServiceFailedDuringExecutionError,
         ServiceUnavailableError, TimedOutError, UnableToConnectToRobotError, UnauthenticatedError,
    -    UnknownDnsNameError, UnimplementedError, TransientFailureError)
    +    UnknownDnsNameError, UnimplementedError, TooManyRequestsError, TransientFailureError)
     
     TransportError = grpc.RpcError
     
    @@ -21,7 +21,7 @@
     
     # Set default max message length for sending and receiving to 100MB. This value is used when
     # creating channels in the bosdyn.client.Robot class.
    -DEFAULT_MAX_MESSAGE_LENGTH = 100 * (1024 ** 2)
    +DEFAULT_MAX_MESSAGE_LENGTH = 100 * (1024**2)
     
     
     class RefreshingAccessTokenAuthMetadataPlugin(grpc.AuthMetadataPlugin):
    @@ -31,6 +31,7 @@ class RefreshingAccessTokenAuthMetadataPlugin(grpc.AuthMetadataPlugin):
             token_cb: Callable that returns a tuple of (app_token, user_token)
             add_app_token (bool): Whether to include an app token in the metadata. This is necessary for compatibility with old robot software.
         """
    +
         def __init__(self, token_cb, add_app_token):
             self._token_cb = token_cb
             self._add_app_token = add_app_token
    @@ -125,6 +126,8 @@ def translate_exception(rpc_error):
                 return InvalidAppTokenError(rpc_error, InvalidAppTokenError.__doc__)
             elif str(404) in details:
                 return NotFoundError(rpc_error, NotFoundError.__doc__)
    +        elif str(429) in details:
    +            return TooManyRequestsError(rpc_error, TooManyRequestsError.__doc__)
             elif str(502) in details:
                 return ServiceUnavailableError(rpc_error, ServiceUnavailableError.__doc__)
             elif str(504) in details:
    @@ -161,16 +164,19 @@ def translate_exception(rpc_error):
             elif 'Connect Failed' in debug or 'Failed to pick subchannel' in debug:
                 # This error should be checked last because a lot of grpc errors contain said substrings.
                 return UnableToConnectToRobotError(rpc_error, UnableToConnectToRobotError.__doc__)
    -    
    +
         if code is grpc.StatusCode.UNAVAILABLE:
    -        if 'Socket closed' in debug:
    +        if 'Socket closed' in debug or 'Connection reset by peer' in debug:
                 return RetryableUnavailableError(rpc_error, RetryableUnavailableError.__doc__)
    +        if str(502) in details:
    +            return ServiceUnavailableError(rpc_error, ServiceUnavailableError.__doc__)
     
         _LOGGER.warning('Unclassified exception: %s', rpc_error)
     
         return RpcError(rpc_error, RpcError.__doc__)
     
    -def generate_channel_options(max_send_message_length = None, max_receive_message_length = None):
    +
    +def generate_channel_options(max_send_message_length=None, max_receive_message_length=None):
         """Generate the array of options to specify in the creation of a client channel or server.
     
         The list contains the values for max allowed message length for both sending and
    @@ -185,5 +191,5 @@ def generate_channel_options(max_send_message_length = None, max_receive_message
         """
     
         return [('grpc.max_send_message_length', max_send_message_length or DEFAULT_MAX_MESSAGE_LENGTH),
    -        ('grpc.max_receive_message_length',
    -            max_receive_message_length or DEFAULT_MAX_MESSAGE_LENGTH)]
    +            ('grpc.max_receive_message_length', max_receive_message_length or
    +             DEFAULT_MAX_MESSAGE_LENGTH)]
    diff --git a/python/bosdyn-client/src/bosdyn/client/command_line.py b/python/bosdyn-client/src/bosdyn/client/command_line.py
    index eb0cb771d..762d107fd 100644
    --- a/python/bosdyn-client/src/bosdyn/client/command_line.py
    +++ b/python/bosdyn-client/src/bosdyn/client/command_line.py
    @@ -17,7 +17,7 @@
     
     import six
     
    -from bosdyn.api.data_buffer_pb2 import TextMessage
    +from bosdyn.api.data_buffer_pb2 import Event, TextMessage
     from bosdyn.api.data_index_pb2 import EventsCommentsSpec
     from bosdyn.api import data_acquisition_pb2
     import bosdyn.client
    @@ -905,8 +905,8 @@ def _get_comments(response):
         def pretty_print(self, values):  # pylint: disable=no-self-use
             last_date_shown = None
             for comment in values:
    -            dtm = datetime.datetime.fromtimestamp(
    -                comment.timestamp.seconds + comment.timestamp.seconds * 1e-9)
    +            dtm = datetime.datetime.fromtimestamp(comment.timestamp.seconds +
    +                                                  comment.timestamp.seconds * 1e-9)
                 if dtm.date() != last_date_shown:
                     print("\n[{}]".format(dtm.date()))
                     last_date_shown = dtm.date()
    @@ -918,6 +918,21 @@ class GetDataBufferEventsCommand(GetDataBufferEventsCommentsCommand):
     
         NAME = 'events'
     
    +    def __init__(self, subparsers, command_dict):
    +        """Get operator comments from the robot.
    +
    +        Args:
    +            subparsers: List of argument parsers.
    +            command_dict: Dictionary of command names which take parsed options.
    +        """
    +        super(GetDataBufferEventsCommand, self).__init__(subparsers, command_dict)
    +        self._parser.add_argument('--type', help='query for only the given event-type')
    +        # pylint: disable=no-member
    +        self._parser.add_argument(
    +            '--level',
    +            choices=Event.Level.keys()[1:],  # slice skips UNSET
    +            help='limit level to this and above')
    +
         def _run(self, robot, options):
             """Implementation of the command.
     
    @@ -930,7 +945,11 @@ def _run(self, robot, options):
             """
     
             request_spec = EventsCommentsSpec()
    -        request_spec.events.add()  # pylint: disable=no-member
    +        event_spec = request_spec.events.add()  # pylint: disable=no-member
    +        if options.type:
    +            event_spec.type = options.type
    +        if options.level:
    +            event_spec.level.value = Event.Level.Value(options.level)  # pylint: disable=no-member
     
             def _get_events(response):
                 return response.events_comments.events
    @@ -1283,6 +1302,8 @@ def __init__(self, subparsers, command_dict):
             super(LicenseCommand, self).__init__(subparsers, command_dict)
             self._parser.add_argument('--proto', action='store_true',
                                       help='print listing in proto format')
    +        self._parser.add_argument('-f', '--feature-codes', nargs='+',
    +                                  help='Optional feature list for GetFeatureEnabled API.')
     
         def _run(self, robot, options):
             """Implementation of the command.
    @@ -1295,13 +1316,27 @@ def _run(self, robot, options):
                 True.
             """
             license_client = robot.ensure_client(LicenseClient.default_service_name)
    +        self._get_license_info(license_client, options)
    +        self._get_feature_enabled(license_client, options)
    +        return True
    +
    +    def _get_license_info(self, license_client, options):
             license_info = license_client.get_license_info()
             if options.proto:
                 print(license_info)
             else:
                 print(str(license_info))
     
    -        return True
    +    def _get_feature_enabled(self, license_client, options):
    +        if not options.feature_codes or len(options.feature_codes) == 0:
    +            return
    +
    +        feature_enabled = license_client.get_feature_enabled(options.feature_codes)
    +        for feature in feature_enabled:
    +            if feature_enabled[feature]:
    +                print(f"Feature {feature} is enabled.")
    +            else:
    +                print(f"Feature {feature} is not enabled.")
     
     
     class LeaseCommands(Subcommands):
    @@ -1792,7 +1827,7 @@ def _run(self, robot, options):
             self._format_and_print_capability("Data Type", "Data Name", "(optional) Service Name")
             print("-" * (self._data_type_width + self._data_name_width + self._service_name_width))
             for data_name in capabilities.data_sources:
    -            self._format_and_print_capability("data", data_name.name)
    +            self._format_and_print_capability("data", data_name.name, data_name.service_name)
             for img_service in capabilities.image_sources:
                 for img in img_service.image_source_names:
                     self._format_and_print_capability("image", img, img_service.service_name)
    @@ -1829,6 +1864,7 @@ def _run(self, robot, options):
             print(response)
             return True
     
    +
     class HostComputerIPCommand(Command):
         """Determine a computer's IP address."""
     
    @@ -1854,7 +1890,9 @@ def _run(self, robot, options):
             Returns:
                 True
             """
    -        print("The IP address of the computer used to talk to the robot is: %s" %(bosdyn.client.common.get_self_ip(robot._name)))
    +        print("The IP address of the computer used to talk to the robot is: %s" %
    +              (bosdyn.client.common.get_self_ip(robot._name)))
    +
     
     
     class PowerCommand(Subcommands):
    diff --git a/python/bosdyn-client/src/bosdyn/client/common.py b/python/bosdyn-client/src/bosdyn/client/common.py
    index f839e75de..4709979df 100644
    --- a/python/bosdyn-client/src/bosdyn/client/common.py
    +++ b/python/bosdyn-client/src/bosdyn/client/common.py
    @@ -8,8 +8,10 @@
     import copy
     import functools
     import logging
    +import math
     import types
     import grpc
    +import six
     import socket
     
     from .channel import TransportError, translate_exception
    @@ -17,10 +19,12 @@
     
     _LOGGER = logging.getLogger(__name__)
     
    +from bosdyn.api import data_chunk_pb2
     from bosdyn.api import license_pb2
     
     DEFAULT_RPC_TIMEOUT = 30  # seconds
     
    +
     def common_header_errors(response):
         """Return an exception based on common response header. None if no error."""
         if response.header.error.code == response.header.error.CODE_UNSPECIFIED:
    @@ -187,6 +191,10 @@ def wrapper(*args, **kwargs):
     
         return wrapper
     
    +def maybe_raise(exc):
    +    """raise the provided exception if it is not None"""
    +    if exc is not None:
    +        raise exc
     
     def print_response(func):
         """Decorate "error from response" functions to print for debugging specific messages."""
    @@ -305,7 +313,9 @@ def update_response_iterator(self, response_iterator, logger, rpc_method, is_blo
                 # be thrown for streaming rpcs if any are going to occur.
                 # Here we make sure that they're translated to our more meaningful exceptions.
                 # Any ResponseErrors or other exception types can be let through untranslated.
    -            raise translate_exception(e)
    +            # Use the "raise from None" pattern to reset the exception's context, which produces
    +            # confusing stack traces.
    +            six.raise_from(translate_exception(e), None)
     
         @process_kwargs
         def call(self, rpc_method, request, value_from_response=None, error_from_response=None,
    @@ -330,14 +340,16 @@ def call(self, rpc_method, request, value_from_response=None, error_from_respons
                 timeout = kwargs.pop('timeout', DEFAULT_RPC_TIMEOUT)
                 response = rpc_method(request, timeout=timeout, **kwargs)
             except TransportError as e:
    -            raise translate_exception(e)
    +            # Use the "raise from None" pattern to reset the exception's context, which produces
    +            # confusing stack traces.
    +            six.raise_from(translate_exception(e), None)
     
             if isinstance(rpc_method, grpc.UnaryStreamMultiCallable) or isinstance(
                     rpc_method, grpc.StreamStreamMultiCallable):
                 # The outgoing response is a streaming response.
                 response = self.update_response_iterator(response, logger, rpc_method, is_blocking=True)
    -            return self.handle_response_streaming(
    -                list(response), error_from_response, value_from_response)
    +            return self.handle_response_streaming(list(response), error_from_response,
    +                                                  value_from_response)
             else:
                 response = self._apply_response_processors(response)
                 logger.debug('response: %s %s', rpc_method._method,
    @@ -350,7 +362,7 @@ def handle_response(self, response, error_from_response, value_from_response):
             else:
                 exc = None
             if exc is not None:
    -            raise exc
    +            raise exc  # pylint: disable=raising-bad-type
             if value_from_response is None:
                 return response
             return value_from_response(response)
    @@ -361,7 +373,7 @@ def handle_response_streaming(self, response, error_from_response, value_from_re
             else:
                 exc = None
             if exc is not None:
    -            raise exc
    +            raise exc  # pylint: disable=raising-bad-type
             if value_from_response is None:
                 return response
             return value_from_response(response)
    @@ -420,6 +432,22 @@ def _get_logger(self, rpc_method):
                 return self.logger.getChild(method_name_short)
             return self.logger
     
    +    @staticmethod
    +    def chunk_message(message, data_chunk_byte_size):
    +        """Take a message, and split it into data chunks
    +        Args:
    +            data_chunk_byte_size: max size of each streamed message
    +        """
    +        serialized = message.SerializeToString()
    +        total_bytes_size = len(serialized)
    +        num_chunks = math.ceil(total_bytes_size / data_chunk_byte_size)
    +        for i in range(num_chunks):
    +            start_index = i * data_chunk_byte_size
    +            end_index = min(total_bytes_size, (i + 1) * data_chunk_byte_size)
    +            chunk = data_chunk_pb2.DataChunk(total_size=total_bytes_size)
    +            chunk.data = serialized[start_index:end_index]
    +            yield chunk
    +
     
     class FutureWrapper():
         """Wraps a Future to aid more complicated clients' async calls."""
    @@ -487,4 +515,4 @@ def get_self_ip(robot_hostname):
             ip = '127.0.0.1'
         finally:
             s.close()
    -    return ip
    \ No newline at end of file
    +    return ip
    diff --git a/python/bosdyn-client/src/bosdyn/client/data_acquisition.py b/python/bosdyn-client/src/bosdyn/client/data_acquisition.py
    index 0d04223e7..331e28335 100644
    --- a/python/bosdyn-client/src/bosdyn/client/data_acquisition.py
    +++ b/python/bosdyn-client/src/bosdyn/client/data_acquisition.py
    @@ -16,9 +16,8 @@
     from google.protobuf import json_format
     
     from bosdyn.client.exceptions import Error, ResponseError
    -from bosdyn.client.common import (common_header_errors, error_factory,
    -                                  handle_common_header_errors, handle_unset_status_error,
    -                                  error_pair, BaseClient)
    +from bosdyn.client.common import (common_header_errors, error_factory, handle_common_header_errors,
    +                                  handle_unset_status_error, error_pair, BaseClient)
     from bosdyn.api import data_acquisition_pb2 as data_acquisition
     from bosdyn.api import data_acquisition_service_pb2_grpc as data_acquisition_service
     
    @@ -61,6 +60,20 @@ def update_from(self, other):
             except AttributeError:
                 pass  # other doesn't have a time_sync accessor
     
    +    def make_acquire_data_request(self, acquisition_requests, action_name, group_name, data_timestamp=None, metadata=None):
    +        """Helper utility to generate an AcquireDataRequest."""
    +        if data_timestamp is None:
    +            if not self._timesync_endpoint:
    +                data_timestamp = now_timestamp()
    +            else:
    +                data_timestamp = self._timesync_endpoint.robot_timestamp_from_local_secs(
    +                    time.time())
    +        action_id = data_acquisition.CaptureActionId(action_name=action_name, group_name=group_name,
    +                                                     timestamp=data_timestamp)
    +        return data_acquisition.AcquireDataRequest(acquisition_requests=acquisition_requests,
    +                                                   action_id=action_id,
    +                                                   metadata=metadata_to_proto(metadata))
    +
         def acquire_data(self, acquisition_requests, action_name, group_name, data_timestamp=None,
                          metadata=None, **kwargs):
             """Trigger a data acquisition to save data and metadata to the data buffer.
    @@ -78,48 +91,40 @@ def acquire_data(self, acquisition_requests, action_name, group_name, data_times
     
             Raises:
               RpcError: Problem communicating with the robot.
    +          ValueError: Metadata is not in the right format.
     
             Returns:
                 If the RPC is successful, then it will return the acquire data request id, which can be
                 used to check the status of the acquisition and get feedback.
             """
    -
    -        if data_timestamp is None:
    -            if not self._timesync_endpoint:
    -                data_timestamp = now_timestamp()
    -            else:
    -                data_timestamp = self._timesync_endpoint.robot_timestamp_from_local_secs(
    -                    time.time())
    -        action_id = data_acquisition.CaptureActionId(action_name=action_name,
    -            group_name=group_name, timestamp=data_timestamp)
    -
    -        metadata_proto = metadata_to_proto(metadata)
    -        request = data_acquisition.AcquireDataRequest(metadata=metadata_proto,
    -                                                      acquisition_requests=acquisition_requests,
    -                                                      action_id=action_id)
    +        request = self.make_acquire_data_request(acquisition_requests, action_name, group_name, 
    +                                            data_timestamp, metadata)
             return self.call(self._stub.AcquireData, request, value_from_response=get_request_id,
                              error_from_response=acquire_data_error, **kwargs)
     
    -    def acquire_data_async(self, acquisition_requests, action_name, group_name,
    -                           data_timestamp=None, metadata=None, **kwargs):
    +    def acquire_data_async(self, acquisition_requests, action_name, group_name, data_timestamp=None,
    +                           metadata=None, **kwargs):
             """Async version of the acquire_data() RPC."""
    -        if data_timestamp is None:
    -            if not self._timesync_endpoint:
    -                data_timestamp = now_timestamp()
    -            else:
    -                data_timestamp = self._timesync_endpoint.robot_timestamp_from_local_secs(
    -                    time.time())
    -        action_id = data_acquisition.CaptureActionId(action_name=action_name,
    -            group_name=group_name, timestamp=data_timestamp)
    +        request = self.make_acquire_data_request(acquisition_requests, action_name, group_name, 
    +                                            data_timestamp, metadata)
    +        return self.call_async(self._stub.AcquireData, request, value_from_response=get_request_id,
    +                               error_from_response=acquire_data_error, **kwargs)
    +
    +    def acquire_data_from_request(self, request, **kwargs):
    +        """Alternate version of acquire_data() that takes an AcquireDataRequest directly.
    +           
    +        Returns:
    +            If the RPC is successful, then it will return the AcquireDataResponse.
    +        """
    +        return self.call(self._stub.AcquireData, request, 
    +                         error_from_response=acquire_data_error, **kwargs)
     
    -        metadata_proto = metadata_to_proto(metadata)
    -        request = data_acquisition.AcquireDataRequest(metadata=metadata_proto,
    -                                                      acquisition_requests=acquisition_requests,
    -                                                      action_id=action_id)
    +    def acquire_data_from_request_async(self, request, **kwargs):
    +        """Async version of acquire_data_from_request()."""
             return self.call_async(self._stub.AcquireData, request,
    -                               value_from_response=get_request_id,
                                    error_from_response=acquire_data_error, **kwargs)
     
    +
         def get_status(self, request_id, **kwargs):
             """Check the status of a data acquisition based on the request id.
     
    @@ -141,8 +146,8 @@ def get_status(self, request_id, **kwargs):
         def get_status_async(self, request_id, **kwargs):
             """Async version of the get_status() RPC."""
             request = data_acquisition.GetStatusRequest(request_id=request_id)
    -        return self.call_async(self._stub.GetStatus, request,
    -                               error_from_response=_get_status_error, **kwargs)
    +        return self.call_async(self._stub.GetStatus, request, error_from_response=_get_status_error,
    +                               **kwargs)
     
         def get_service_info(self, **kwargs):
             """Get information from a DAQ service to list it's capabilities - which data, metadata,
    @@ -180,8 +185,8 @@ def cancel_acquisition(self, request_id, **kwargs):
                 status as well as other information about any possible errors.
             """
             request = data_acquisition.CancelAcquisitionRequest(request_id=request_id)
    -        return self.call(self._stub.CancelAcquisition, request, error_from_response=_cancel_acquisition_error,
    -                         **kwargs)
    +        return self.call(self._stub.CancelAcquisition, request,
    +                         error_from_response=_cancel_acquisition_error, **kwargs)
     
         def cancel_acquisition_async(self, request_id, **kwargs):
             """Async version of the cancel_acquisition() RPC."""
    @@ -190,8 +195,8 @@ def cancel_acquisition_async(self, request_id, **kwargs):
                                    error_from_response=_cancel_acquisition_error, **kwargs)
     
     
    -_ACQUIRE_DATA_STATUS_TO_ERROR = collections.defaultdict(
    -    lambda: (DataAcquisitionResponseError, None))
    +_ACQUIRE_DATA_STATUS_TO_ERROR = collections.defaultdict(lambda:
    +                                                        (DataAcquisitionResponseError, None))
     
     _ACQUIRE_DATA_STATUS_TO_ERROR.update({
         data_acquisition.AcquireDataResponse.STATUS_OK: (None, None),
    @@ -213,6 +218,7 @@ def cancel_acquisition_async(self, request_id, **kwargs):
             error_pair(CancellationFailedError)
     })
     
    +
     def metadata_to_proto(metadata):
         """Checks the type to determine if a conversion is required to create a
         bosdyn.api.Metadata proto message.
    @@ -222,16 +228,23 @@ def metadata_to_proto(metadata):
                 with the data returned by the DataAcquisitionService when logged in the data buffer
                 service.
     
    +    Raises:
    +        ValueError: Metadata is not in the right format.
    +
         Returns:
             If metadata is provided, this will return a protobuf Metadata message. Otherwise it will
             return None.
         """
    +    if metadata is None:
    +        return None
         metadata_proto = None
         if isinstance(metadata, data_acquisition.Metadata):
             metadata_proto = metadata
         elif isinstance(metadata, dict):
             metadata_proto = data_acquisition.Metadata()
             metadata_proto.data.update(metadata)
    +    else:
    +        raise ValueError('Invalid metadata, not a dict or data_acquisition.Metadata')
         return metadata_proto
     
     
    @@ -240,8 +253,8 @@ def metadata_to_proto(metadata):
     def acquire_data_error(response):
         """Return a custom exception based on the AcquireData response, None if no error."""
         return error_factory(response, response.status,
    -                        status_to_string=data_acquisition.AcquireDataResponse.Status.Name,
    -                        status_to_error=_ACQUIRE_DATA_STATUS_TO_ERROR)
    +                         status_to_string=data_acquisition.AcquireDataResponse.Status.Name,
    +                         status_to_error=_ACQUIRE_DATA_STATUS_TO_ERROR)
     
     
     @handle_common_header_errors
    @@ -249,19 +262,22 @@ def acquire_data_error(response):
     def _get_status_error(response):
         """Return a custom exception based on the GetStatus response, None if no error."""
         return error_factory(response, response.status,
    -                        status_to_string=data_acquisition.GetStatusResponse.Status.Name,
    -                        status_to_error=_GET_STATUS_STATUS_TO_ERROR)
    +                         status_to_string=data_acquisition.GetStatusResponse.Status.Name,
    +                         status_to_error=_GET_STATUS_STATUS_TO_ERROR)
    +
     
     @handle_common_header_errors
     @handle_unset_status_error(unset='STATUS_UNKNOWN')
     def _cancel_acquisition_error(response):
         """Return a custom exception based on the CancelAcquisition response, None if no error."""
         return error_factory(response, response.status,
    -                        status_to_string=data_acquisition.CancelAcquisitionResponse.Status.Name,
    -                        status_to_error=_CANCEL_ACQUISITION_STATUS_TO_ERROR)
    +                         status_to_string=data_acquisition.CancelAcquisitionResponse.Status.Name,
    +                         status_to_error=_CANCEL_ACQUISITION_STATUS_TO_ERROR)
    +
     
     def _get_service_info_capabilities(response):
         return response.capabilities
     
    +
     def get_request_id(response):
         return response.request_id
    diff --git a/python/bosdyn-client/src/bosdyn/client/data_acquisition_helpers.py b/python/bosdyn-client/src/bosdyn/client/data_acquisition_helpers.py
    index bd37d809c..0b517f9f9 100644
    --- a/python/bosdyn-client/src/bosdyn/client/data_acquisition_helpers.py
    +++ b/python/bosdyn-client/src/bosdyn/client/data_acquisition_helpers.py
    @@ -26,8 +26,9 @@
     # Logger for all the debug information from the tests.
     _LOGGER = logging.getLogger()
     
    -def issue_acquire_data_request(data_acq_client, acquisition_requests, group_name,
    -	                           action_name, metadata=None):
    +
    +def issue_acquire_data_request(data_acq_client, acquisition_requests, group_name, action_name,
    +                               metadata=None):
         """Sends the data acquisition request without blocking until the acquisition completes.
     
         Args:
    @@ -42,19 +43,21 @@ def issue_acquire_data_request(data_acq_client, acquisition_requests, group_name
             indicates the AcquireData rpc failed.
         """
         # Create action id for the query for this request.
    -    action_id = data_acquisition_pb2.CaptureActionId(action_name=action_name,
    -                                                     group_name=group_name)
    +    action_id = data_acquisition_pb2.CaptureActionId(action_name=action_name, group_name=group_name)
     
         # Send an AcquireData request
         request_id = None
         try:
             request_id = data_acq_client.acquire_data(acquisition_requests=acquisition_requests,
    -            action_name=action_name, group_name=action_id.group_name, metadata=metadata)
    +                                                  action_name=action_name,
    +                                                  group_name=action_id.group_name,
    +                                                  metadata=metadata)
         except ResponseError as err:
             print("Exception raised by issue_acquire_data_request: " + str(err))
     
         return request_id, action_id
     
    +
     def acquire_and_process_request(data_acquisition_client, acquisition_requests, group_name,
                                     action_name, metadata=None, block_until_complete=True):
         """Send acquisition request and optionally block until the acquisition completes.
    @@ -74,8 +77,9 @@ def acquire_and_process_request(data_acquisition_client, acquisition_requests, g
             Boolean indicating if the acquisition completed successfully or not.
         """
         # Make the acquire data request. This will return our current request id.
    -    request_id, action_id = issue_acquire_data_request(data_acquisition_client, acquisition_requests,
    -        group_name, action_name, metadata)
    +    request_id, action_id = issue_acquire_data_request(data_acquisition_client,
    +                                                       acquisition_requests, group_name,
    +                                                       action_name, metadata)
     
         if not request_id:
             # The AcquireData request failed for some reason. No need to attempt to
    @@ -95,7 +99,7 @@ def acquire_and_process_request(data_acquisition_client, acquisition_requests, g
                 print("Exception: %s" % str(err))
                 return False
             print("Current status is: %s" %
    -            data_acquisition_pb2.GetStatusResponse.Status.Name(get_status_response.status))
    +              data_acquisition_pb2.GetStatusResponse.Status.Name(get_status_response.status))
             if get_status_response.status == data_acquisition_pb2.GetStatusResponse.STATUS_COMPLETE:
                 return True
             if get_status_response.status == data_acquisition_pb2.GetStatusResponse.STATUS_TIMEDOUT:
    @@ -105,11 +109,13 @@ def acquire_and_process_request(data_acquisition_client, acquisition_requests, g
                 print("Data error was received: %s" % get_status_response)
                 return False
             if get_status_response.status == data_acquisition_pb2.GetStatusResponse.STATUS_REQUEST_ID_DOES_NOT_EXIST:
    -            print("The acquisition request id %s is unknown: %s" % (request_id, get_status_response))
    +            print(
    +                "The acquisition request id %s is unknown: %s" % (request_id, get_status_response))
                 return False
             time.sleep(0.2)
         return True
     
    +
     def cancel_acquisition_request(data_acq_client, request_id):
         """Cancels an acquisition request based on the request id
     
    @@ -128,9 +134,10 @@ def cancel_acquisition_request(data_acq_client, request_id):
         try:
             is_cancelled_response = data_acq_client.cancel_acquisition(request_id)
             print("Status of the request to cancel the data-acquisition in progress: " +
    -            data_acquisition_pb2.CancelAcquisitionResponse.Status.Name(is_cancelled_response.status))
    +              data_acquisition_pb2.CancelAcquisitionResponse.Status.Name(
    +                  is_cancelled_response.status))
         except ResponseError as err:
    -        print("ResponseError raised when cancelling: "+str(err))
    +        print("ResponseError raised when cancelling: " + str(err))
             # Don't attempt to wait for the cancellation success status.
             return
     
    @@ -144,11 +151,12 @@ def cancel_acquisition_request(data_acq_client, request_id):
                 break
     
             print("Request " + str(request_id) + " status: " +
    -            data_acquisition_pb2.GetStatusResponse.Status.Name(get_status_response.status))
    +              data_acquisition_pb2.GetStatusResponse.Status.Name(get_status_response.status))
             if get_status_response.status == data_acquisition_pb2.GetStatusResponse.STATUS_ACQUISITION_CANCELLED:
                 print("The request is fully cancelled.")
                 break
     
    +
     def clean_filename(filename):
         """Removes bad characters in a filename.
     
    @@ -161,6 +169,7 @@ def clean_filename(filename):
     
         return "".join(i for i in filename if i not in ":*?<>|")
     
    +
     def make_time_query_params(start_time_secs, end_time_secs, robot):
         """Create time-based query params for the download request.
     
    @@ -177,9 +186,10 @@ def make_time_query_params(start_time_secs, end_time_secs, robot):
         print(from_timestamp.ToJsonString(), to_timestamp.ToJsonString())
         query_params = data_acquisition_store_pb2.DataQueryParams(
             time_range=data_acquisition_store_pb2.TimeRangeQuery(from_timestamp=from_timestamp,
    -            to_timestamp=to_timestamp))
    +                                                             to_timestamp=to_timestamp))
         return query_params
     
    +
     def make_time_query_params_from_group_name(group_name, data_store_client):
         """Create time-based query params for the download request using the group name.
     
    @@ -198,7 +208,8 @@ def make_time_query_params_from_group_name(group_name, data_store_client):
         try:
             saved_capture_actions = data_store_client.list_capture_actions(query_params)
         except Exception as err:
    -        _LOGGER.error("Failed to list the capture action ids for group_name %s: %s", group_name, err)
    +        _LOGGER.error("Failed to list the capture action ids for group_name %s: %s", group_name,
    +                      err)
             return None
     
         # Filter all the CaptureActionIds for the start/end time. These end times are already in
    @@ -218,7 +229,8 @@ def make_time_query_params_from_group_name(group_name, data_store_client):
                 end_time = (time_secs, timestamp)
     
         if not (start_time and end_time):
    -        _LOGGER.error("Could not find a start/end time from the list of capture action ids: %s", saved_capture_actions)
    +        _LOGGER.error("Could not find a start/end time from the list of capture action ids: %s",
    +                      saved_capture_actions)
             return None
     
         # Ensure the timestamps are ordered correctly and the
    @@ -228,14 +240,16 @@ def make_time_query_params_from_group_name(group_name, data_store_client):
         start_time[1].seconds -= 3
         end_time[1].seconds += 3
     
    -    _LOGGER.info("Downloading data with a start time  of %s seconds and end time of %s seconds.", start_time[0], end_time[0])
    +    _LOGGER.info("Downloading data with a start time  of %s seconds and end time of %s seconds.",
    +                 start_time[0], end_time[0])
     
         # Make the download data request with a time query parameter.
         query_params = data_acquisition_store_pb2.DataQueryParams(
             time_range=data_acquisition_store_pb2.TimeRangeQuery(from_timestamp=start_time[1],
    -                                                                to_timestamp=end_time[1]))
    +                                                             to_timestamp=end_time[1]))
         return query_params
     
    +
     def download_data_REST(query_params, hostname, token, destination_folder='.',
                            additional_params=None):
         """Retrieve all data for a query from the DataBuffer REST API and write it to files.
    @@ -261,18 +275,20 @@ def download_data_REST(query_params, hostname, token, destination_folder='.',
             headers = {"Authorization": "Bearer {}".format(token)}
             get_params = additional_params or {}
             if query_params.HasField('time_range'):
    -            get_params.update({'from_nsec': query_params.time_range.from_timestamp.ToNanoseconds(),
    -                'to_nsec': query_params.time_range.to_timestamp.ToNanoseconds()})
    -        chunk_size = 10 * (1024 ** 2) # This value is not guaranteed.
    +            get_params.update({
    +                'from_nsec': query_params.time_range.from_timestamp.ToNanoseconds(),
    +                'to_nsec': query_params.time_range.to_timestamp.ToNanoseconds()
    +            })
    +        chunk_size = 10 * (1024**2)  # This value is not guaranteed.
     
             with requests.get(url, verify=False, stream=True, headers=headers,
    -            params=get_params) as resp:
    +                          params=get_params) as resp:
                 print("Download request HTTPS status code: %s" % resp.status_code)
                 # This is the default file name used to download data, updated from response.
                 if resp.status_code == 204:
                     print("No content available for the specified download time range (in seconds): "
    -                "[%d, %d]"% (query_params.time_range.from_timestamp.ToNanoseconds()/1.0e9,
    -                query_params.time_range.to_timestamp.ToNanoseconds()/1.0e9))
    +                      "[%d, %d]" % (query_params.time_range.from_timestamp.ToNanoseconds() / 1.0e9,
    +                                    query_params.time_range.to_timestamp.ToNanoseconds() / 1.0e9))
                     return False
                 download_file = Path(folder, "download.zip")
                 content = resp.headers['Content-Disposition']
    @@ -290,7 +306,7 @@ def download_data_REST(query_params, hostname, token, destination_folder='.',
     
                 with open(str(download_file), 'wb') as fid:
                     for chunk in resp.iter_content(chunk_size=chunk_size):
    -                    print('.', end = '', flush=True)
    +                    print('.', end='', flush=True)
                         fid.write(chunk)
         except requests.exceptions.HTTPError as rest_error:
             print("REST Exception:\n")
    diff --git a/python/bosdyn-client/src/bosdyn/client/data_acquisition_plugin.py b/python/bosdyn-client/src/bosdyn/client/data_acquisition_plugin.py
    index 5cddcf09a..22012c276 100644
    --- a/python/bosdyn-client/src/bosdyn/client/data_acquisition_plugin.py
    +++ b/python/bosdyn-client/src/bosdyn/client/data_acquisition_plugin.py
    @@ -52,6 +52,7 @@ def acquire_plugin_data(self, acquisition_requests, action_id, data_identifiers=
     
             Raises:
               RpcError: Problem communicating with the robot.
    +          ValueError: Metadata is not in the right format.
     
             Returns:
                 If the RPC is successful, then it will return the acquire data response, which can be
    @@ -66,7 +67,7 @@ def acquire_plugin_data(self, acquisition_requests, action_id, data_identifiers=
                              error_from_response=acquire_data_error, **kwargs)
     
         def acquire_plugin_data_async(self, acquisition_requests, action_id, data_identifiers=None,
    -        metadata=None, **kwargs):
    +                                  metadata=None, **kwargs):
             """Async version of the acquire_plugin_data() RPC."""
     
             metadata_proto = metadata_to_proto(metadata)
    diff --git a/python/bosdyn-client/src/bosdyn/client/data_acquisition_plugin_service.py b/python/bosdyn-client/src/bosdyn/client/data_acquisition_plugin_service.py
    index a9238bebb..094ef58f5 100644
    --- a/python/bosdyn-client/src/bosdyn/client/data_acquisition_plugin_service.py
    +++ b/python/bosdyn-client/src/bosdyn/client/data_acquisition_plugin_service.py
    @@ -55,8 +55,7 @@
     from bosdyn.client import Robot
     from bosdyn.client.data_acquisition_store import DataAcquisitionStoreClient
     from bosdyn.client.data_buffer import DataBufferClient
    -from bosdyn.client.util import populate_response_header
    -from bosdyn.client.server_util import ResponseContext
    +from bosdyn.client.server_util import ResponseContext, populate_response_header
     
     _LOGGER = logging.getLogger(__name__)
     
    @@ -266,8 +265,8 @@ def wait_for_stores_complete(self):
                 if future.exception() is None:
                     self.state.add_saved([data_id])
                 else:
    -                self.state.add_errors([
    -                    make_error(data_id, 'Failed to store data: {}'.format(future.exception()))])
    +                self.state.add_errors(
    +                    [make_error(data_id, 'Failed to store data: {}'.format(future.exception()))])
     
             return not self.state.has_data_errors()
     
    diff --git a/python/bosdyn-client/src/bosdyn/client/data_acquisition_store.py b/python/bosdyn-client/src/bosdyn/client/data_acquisition_store.py
    index 2440ad781..9af9786f6 100644
    --- a/python/bosdyn-client/src/bosdyn/client/data_acquisition_store.py
    +++ b/python/bosdyn-client/src/bosdyn/client/data_acquisition_store.py
    @@ -16,9 +16,8 @@
     from google.protobuf import json_format
     
     from bosdyn.client.exceptions import Error, ResponseError
    -from bosdyn.client.common import (common_header_errors, error_factory,
    -                                  handle_common_header_errors, handle_unset_status_error,
    -                                  error_pair, BaseClient)
    +from bosdyn.client.common import (common_header_errors, error_factory, handle_common_header_errors,
    +                                  handle_unset_status_error, error_pair, BaseClient)
     from bosdyn.api import data_acquisition_store_pb2 as data_acquisition_store
     from bosdyn.api import data_acquisition_store_service_pb2_grpc as data_acquisition_store_service
     from bosdyn.api import image_pb2
    @@ -46,7 +45,6 @@ def update_from(self, other):
             except AttributeError:
                 pass  # other doesn't have a time_sync accessor
     
    -
         def list_capture_actions(self, query, **kwargs):
             """List capture actions that satisfy the query parameters.
     
    @@ -69,7 +67,6 @@ def list_capture_actions_async(self, query, **kwargs):
                                    value_from_response=_get_action_ids,
                                    error_from_response=common_header_errors, **kwargs)
     
    -
         def list_stored_images(self, query, **kwargs):
             """List images that satisfy the query parameters.
     
    @@ -91,7 +88,6 @@ def list_stored_images_async(self, query, **kwargs):
                                    value_from_response=_get_data_ids,
                                    error_from_response=common_header_errors, **kwargs)
     
    -
         def list_stored_metadata(self, query, **kwargs):
             """List metadata that satisfy the query parameters.
     
    @@ -113,7 +109,6 @@ def list_stored_metadata_async(self, query, **kwargs):
                                    value_from_response=_get_data_ids,
                                    error_from_response=common_header_errors, **kwargs)
     
    -
         def list_stored_data(self, query, **kwargs):
             """List data that satisfy the query parameters.
     
    @@ -135,7 +130,6 @@ def list_stored_data_async(self, query, **kwargs):
                                    value_from_response=_get_data_ids,
                                    error_from_response=common_header_errors, **kwargs)
     
    -
         def store_image(self, image, data_id, **kwargs):
             """Store image.
     
    @@ -148,9 +142,8 @@ def store_image(self, image, data_id, **kwargs):
             """
     
             request = data_acquisition_store.StoreImageRequest(image=image, data_id=data_id)
    -        return self.call(self._stub.StoreImage, request,
    -                         error_from_response=common_header_errors, **kwargs)
    -
    +        return self.call(self._stub.StoreImage, request, error_from_response=common_header_errors,
    +                         **kwargs)
     
         def store_image_async(self, image, data_id, **kwargs):
             """Async version of the store_image() RPC."""
    @@ -158,7 +151,6 @@ def store_image_async(self, image, data_id, **kwargs):
             return self.call_async(self._stub.StoreImage, request,
                                    error_from_response=common_header_errors, **kwargs)
     
    -
         def store_metadata(self, associated_metadata, data_id, **kwargs):
             """Store metadata.
     
    @@ -174,19 +166,17 @@ def store_metadata(self, associated_metadata, data_id, **kwargs):
             """
     
             request = data_acquisition_store.StoreMetadataRequest(metadata=associated_metadata,
    -            data_id=data_id)
    +                                                              data_id=data_id)
             return self.call(self._stub.StoreMetadata, request,
                              error_from_response=common_header_errors, **kwargs)
     
    -
         def store_metadata_async(self, associated_metadata, data_id, **kwargs):
             """Async version of the store_metadata() RPC."""
             request = data_acquisition_store.StoreMetadataRequest(metadata=associated_metadata,
    -            data_id=data_id)
    +                                                              data_id=data_id)
             return self.call_async(self._stub.StoreMetadata, request,
                                    error_from_response=common_header_errors, **kwargs)
     
    -
         def store_data(self, data, data_id, file_extension=None, **kwargs):
             """Store data.
     
    @@ -200,15 +190,14 @@ def store_data(self, data, data_id, file_extension=None, **kwargs):
             """
     
             request = data_acquisition_store.StoreDataRequest(data=data, data_id=data_id,
    -            file_extension=file_extension)
    -        return self.call(self._stub.StoreData, request,
    -                         error_from_response=common_header_errors, **kwargs)
    -
    +                                                          file_extension=file_extension)
    +        return self.call(self._stub.StoreData, request, error_from_response=common_header_errors,
    +                         **kwargs)
     
         def store_data_async(self, data, data_id, file_extension=None, **kwargs):
             """Async version of the store_data() RPC."""
             request = data_acquisition_store.StoreDataRequest(data=data, data_id=data_id,
    -            file_extension=file_extension)
    +                                                          file_extension=file_extension)
             return self.call_async(self._stub.StoreData, request,
                                    error_from_response=common_header_errors, **kwargs)
     
    @@ -216,11 +205,14 @@ def store_data_async(self, data, data_id, file_extension=None, **kwargs):
     def _get_action_ids(response):
         return response.action_ids
     
    +
     def _get_data_ids(response):
         return response.data_ids
     
    +
     def _get_image(response):
         return response.image
     
    +
     def _get_metadata(response):
         return response.metadata
    diff --git a/python/bosdyn-client/src/bosdyn/client/data_buffer.py b/python/bosdyn-client/src/bosdyn/client/data_buffer.py
    index 971df9676..e435c655a 100644
    --- a/python/bosdyn-client/src/bosdyn/client/data_buffer.py
    +++ b/python/bosdyn-client/src/bosdyn/client/data_buffer.py
    @@ -14,6 +14,7 @@
     
     import functools
     import time
    +import uuid
     
     from bosdyn.client.exceptions import Error
     from bosdyn.client.common import BaseClient, common_header_errors
    @@ -26,6 +27,56 @@ class InvalidArgument(Error):
         """A given argument could not be used."""
     
     
    +def log_event(  # pylint: disable=too-many-arguments,no-member
    +        robot, event_type, level, description, start_timestamp_secs,
    +        end_timestamp_secs=None, id_str=None, parameters=None,
    +        log_preserve_hint=data_buffer_protos.Event.LOG_PRESERVE_HINT_NORMAL):
    +    """Add an Event to the Data Buffer.
    +
    +    Args:
    +      robot:                          A Robot object.
    +      event_type (string):            The type of event.
    +      level (bosdyn.api.Event.Level): The relative importance of the event.
    +      description (string):           A human-readable description of the event.
    +      start_timestamp_secs (float):   Start of the event, in local time.
    +      end_timestamp_secs (float):     End of the event.  start_timestamp_secs is used if None.
    +      id_str (string):                      Unique id for event.  A uuid is generated if None.
    +      parameters ([bosdyn.api.Parameter]):  Parameters to attach to the event.
    +      log_preserve_hint (bosdyn.api.LogPreserveHint): Whether event should try to preserve log data.
    +    """
    +
    +    data_buffer_client = robot.ensure_client(DataBufferClient.default_service_name)
    +
    +    if not id_str:
    +        id_str = str(uuid.uuid1())
    +
    +    robot.time_sync.wait_for_sync()
    +    robot_start_timestamp = robot.time_sync.robot_timestamp_from_local_secs(start_timestamp_secs)
    +    if end_timestamp_secs:
    +        robot_end_timestamp = robot.time_sync.robot_timestamp_from_local_secs(end_timestamp_secs)
    +    else:
    +        robot_end_timestamp = robot_start_timestamp
    +
    +    # pylint: disable=no-member
    +    if isinstance(log_preserve_hint, bool):
    +        if log_preserve_hint:
    +            log_preserve_hint = data_buffer_protos.Event.LOG_PRESERVE_HINT_PRESERVE
    +        else:
    +            log_preserve_hint = data_buffer_protos.Event.LOG_PRESERVE_HINT_NORMAL
    +
    +    event = data_buffer_protos.Event(
    +        type=event_type, description=description, source=robot.client_name,
    +        id=id_str, start_time=robot_start_timestamp, end_time=robot_end_timestamp,
    +        level=level, log_preserve_hint=log_preserve_hint)
    +
    +    if parameters:
    +        for parameter in parameters:
    +            proto = event.parameters.add()
    +            proto.CopyFrom(parameter)
    +
    +    data_buffer_client.add_events([event])
    +
    +
     class DataBufferClient(BaseClient):
         """A client for adding to robot data buffer."""
     
    @@ -65,6 +116,7 @@ def _do_add_text_messages(self, func, text_messages, **kwargs):
             """Internal text message RPC stub call."""
             request = data_buffer_protos.RecordTextMessagesRequest()
             for in_text_msg in text_messages:
    +            # pylint: disable=no-member
                 request.text_messages.add().CopyFrom(in_text_msg)
     
             return func(self._stub.RecordTextMessages, request, value_from_response=None,
    @@ -91,6 +143,7 @@ def _do_add_operator_comment(self, func, msg, robot_timestamp=None, **kwargs):
             """Internal operator comment RPC stub call."""
             request = data_buffer_protos.RecordOperatorCommentsRequest()
             robot_timestamp = robot_timestamp or self._now_in_robot_basis(msg_type="Operator Comment")
    +        # pylint: disable=no-member
             request.operator_comments.add(message=msg, timestamp=robot_timestamp)
             return func(self._stub.RecordOperatorComments, request, value_from_response=None,
                         error_from_response=common_header_errors, **kwargs)
    @@ -101,10 +154,11 @@ def add_blob(self, data, type_id, channel=None, robot_timestamp=None, write_sync
     
             Args:
                 data (bytes): Binary data of one blob.
    -            type_id (string): Type of binary data of blob. For example, this could be the full name of
    -                            a protobuf message type.
    -            channel (string): The name by which messages are typically queried: often the same as
    -                            type_id, or of the form '{prefix}/{type_id}'.
    +            type_id (string): Type of binary data of blob. For example, this could
    +                               be the full name of a protobuf message type.
    +            channel (string): The name by which messages are typically queried:
    +                               often the same as type_id, or of the form
    +                               '{prefix}/{type_id}'.
                 robot_timestamp (google.protobuf.Timestamp): Time of messages, in *robot time*.
     
             Raises:
    @@ -128,8 +182,11 @@ def _do_add_blob(  # pylint: disable=too-many-arguments
                 channel = type_id
     
             robot_timestamp = robot_timestamp or self._now_in_robot_basis(msg_type=type_id)
    -        request.blob_data.add(timestamp=robot_timestamp, channel=channel, type_id=type_id,
    -                              data=data)
    +
    +        request.blob_data.add(  # pylint: disable=no-member
    +            timestamp=robot_timestamp, channel=channel, type_id=type_id, data=data)
    +
    +        request.sync = write_sync
     
             request.sync = write_sync
     
    @@ -183,7 +240,7 @@ def _do_add_events(self, func, events, **kwargs):
             request = data_buffer_protos.RecordEventsRequest()
     
             for event in events:
    -            request.events.add().CopyFrom(event)
    +            request.events.add().CopyFrom(event)  # pylint: disable=no-member
     
             return func(self._stub.RecordEvents, request, value_from_response=None,
                         error_from_response=common_header_errors, **kwargs)
    @@ -192,7 +249,8 @@ def register_signal_schema(self, variables, schema_name, **kwargs):
             """Log signal schema to the robot.
     
             Args:
    -            variables (List[SignalSchema.Variable]): List of SignalSchema variables defining what is in tick.
    +            variables (List[SignalSchema.Variable]): List of SignalSchema variables
    +                                                      defining what is in tick.
                 schema_name (string): Name of schema (defined previously by client).
     
             Raises:
    @@ -208,7 +266,7 @@ def _do_register_signal_schema(self, func, variables, schema_name, **kwargs):
             """Internal register stub call."""
             tick_schema = data_buffer_protos.SignalSchema(vars=variables, schema_name=schema_name)
             request = data_buffer_protos.RegisterSignalSchemaRequest()
    -        request.schema.CopyFrom(tick_schema)
    +        request.schema.CopyFrom(tick_schema)  # pylint: disable=no-member
             # Schemas are saved internally, according to their schema ID. We need to wait for the
             # response from the server to get the schema id. The response does not include the schema
             # itself so use a partial to process the response appropriately.
    @@ -217,8 +275,9 @@ def _do_register_signal_schema(self, func, variables, schema_name, **kwargs):
                         value_from_response=value_from_response,
                         error_from_response=common_header_errors, **kwargs)
     
    -    def add_signal_tick(self, data, schema_id, encoding=data_buffer_protos.SignalTick.ENCODING_RAW,
    -                        sequence_id=0, source="client", **kwargs):
    +    def add_signal_tick(  # pylint: disable=too-many-arguments,no-member
    +            self, data, schema_id, encoding=data_buffer_protos.SignalTick.ENCODING_RAW,
    +            sequence_id=0, source="client", **kwargs):
             """Log signal data to the robot data buffer.
     
             Schema should be sent before any ticks.
    @@ -237,21 +296,23 @@ def add_signal_tick(self, data, schema_id, encoding=data_buffer_protos.SignalTic
             return self._do_add_signal_tick(self.call, data, schema_id, encoding, sequence_id, source,
                                             **kwargs)
     
    -    def add_signal_tick_async(self, data, schema_id,
    -                              encoding=data_buffer_protos.SignalTick.ENCODING_RAW, sequence_id=0,
    -                              source="client", **kwargs):
    +    def add_signal_tick_async(  # pylint: disable=too-many-arguments,no-member
    +            self, data, schema_id, encoding=data_buffer_protos.SignalTick.ENCODING_RAW,
    +            sequence_id=0, source="client", **kwargs):
             """Async version of add_signal_tick."""
             return self._do_add_signal_tick(self.call_async, data, schema_id, encoding, sequence_id,
                                             source, **kwargs)
     
    -    def _do_add_signal_tick(self, func, data, schema_id, encoding, sequence_id, source, **kwargs):
    +    def _do_add_signal_tick(  # pylint: disable=too-many-arguments
    +            self, func, data, schema_id, encoding, sequence_id, source, **kwargs):
             """Internal add signal tick stub call."""
             if schema_id not in self.log_tick_schemas:
                 raise LookupError('The log tick schema id "{}" is unknown'.format(schema_id))
     
             request = data_buffer_protos.RecordSignalTicksRequest()
    -        request.tick_data.add(sequence_id=sequence_id, source=source, schema_id=schema_id,
    -                              encoding=encoding, data=data)
    +        request.tick_data.add(  # pylint: disable=no-member
    +            sequence_id=sequence_id, source=source, schema_id=schema_id, encoding=encoding,
    +            data=data)
             return func(self._stub.RecordSignalTicks, request, value_from_response=None,
                         error_from_response=common_header_errors, **kwargs)
     
    diff --git a/python/bosdyn-client/src/bosdyn/client/data_service.py b/python/bosdyn-client/src/bosdyn/client/data_service.py
    index 1b720fc4a..832fe7509 100644
    --- a/python/bosdyn-client/src/bosdyn/client/data_service.py
    +++ b/python/bosdyn-client/src/bosdyn/client/data_service.py
    @@ -23,7 +23,7 @@ class InvalidArgument(Error):
     class DataServiceClient(BaseClient):
         """A client for adding to robot data buffer."""
     
    -    default_service_name = 'data-service'
    +    default_service_name = 'data'
         service_type = 'bosdyn.api.DataService'
     
         def __init__(self):
    diff --git a/python/bosdyn-client/src/bosdyn/client/directory.py b/python/bosdyn-client/src/bosdyn/client/directory.py
    index 606ae34f1..7ff24a2ea 100644
    --- a/python/bosdyn-client/src/bosdyn/client/directory.py
    +++ b/python/bosdyn-client/src/bosdyn/client/directory.py
    @@ -115,7 +115,7 @@ def get_entry_async(self, service_name, **kwargs):
     _STATUS_TO_ERROR.update({
         directory_pb2.GetServiceEntryResponse.STATUS_OK: (None, None),
         directory_pb2.GetServiceEntryResponse.STATUS_NONEXISTENT_SERVICE:
    -    (NonexistentServiceError, NonexistentServiceError.__doc__),
    +        (NonexistentServiceError, NonexistentServiceError.__doc__),
     })
     
     
    diff --git a/python/bosdyn-client/src/bosdyn/client/directory_registration.py b/python/bosdyn-client/src/bosdyn/client/directory_registration.py
    index 62a11b377..646b2eacd 100644
    --- a/python/bosdyn-client/src/bosdyn/client/directory_registration.py
    +++ b/python/bosdyn-client/src/bosdyn/client/directory_registration.py
    @@ -21,7 +21,6 @@
                                       handle_common_header_errors)
     from .exceptions import ResponseError, TimedOutError, RetryableUnavailableError
     
    -
     _LOGGER = logging.getLogger(__name__)
     
     
    diff --git a/python/bosdyn-client/src/bosdyn/client/docking.py b/python/bosdyn-client/src/bosdyn/client/docking.py
    index 49c92f8bb..53aa3bc85 100644
    --- a/python/bosdyn-client/src/bosdyn/client/docking.py
    +++ b/python/bosdyn-client/src/bosdyn/client/docking.py
    @@ -9,12 +9,17 @@
     import collections
     import time
     
    +from deprecated import deprecated
    +
     from bosdyn.api.docking import docking_pb2, docking_service_pb2_grpc
     from bosdyn.client import lease
    -from bosdyn.client.common import (BaseClient, error_factory, handle_common_header_errors,
    -                                  handle_lease_use_result_errors, handle_unset_status_error)
    +from bosdyn.client.common import (BaseClient, common_header_errors, common_lease_errors,
    +                                  error_factory, handle_common_header_errors,
    +                                  handle_lease_use_result_errors, handle_unset_status_error,
    +                                  maybe_raise)
     from bosdyn.client.exceptions import ResponseError
     from bosdyn.client.robot_command import CommandFailedError
    +from bosdyn.util import now_sec, seconds_to_timestamp
     
     
     class DockingClient(BaseClient):
    @@ -64,7 +69,47 @@ def docking_command_async(self, station_id, clock_identifier, end_time, prep_pos
             return self.call_async(self._stub.DockingCommand, req, self._docking_id_from_response,
                                    _docking_command_error_from_response, **kwargs)
     
    +    def docking_command_full(self, station_id, clock_identifier, end_time, prep_pose_behavior=None,
    +                             lease=None, **kwargs):
    +        """Identical to docking_command(), except will return the full DockingCommandResponse."""
    +        req = self._docking_command_request(lease, station_id, clock_identifier, end_time,
    +                                            prep_pose_behavior)
    +        return self.call(self._stub.DockingCommand, req,
    +                         error_from_response=_docking_command_error_from_response, **kwargs)
    +
    +    def docking_command_full_async(self, station_id, clock_identifier, end_time,
    +                                   prep_pose_behavior=None, lease=None, **kwargs):
    +        """Identical to docking_command_async(), except will return the full DockingCommandResponse."""
    +        req = self._docking_command_request(lease, station_id, clock_identifier, end_time,
    +                                            prep_pose_behavior)
    +        return self.call_async(self._stub.DockingCommand, req,
    +                               error_from_response=_docking_command_error_from_response, **kwargs)
    +
    +
    +    def docking_command_feedback_full(self, command_id, **kwargs):
    +        """Check the status of a previously issued docking command.
    +
    +        Args:
    +            command_id: The ID returned from a previous docking_command call.
    +        Raises:
    +            RpcError: problem communicating with the robot
    +
    +        Returns:
    +            DockingCommandFeedbackResponse
    +        """
    +        req = self._docking_command_feedback_request(command_id)
    +        return self.call(self._stub.DockingCommandFeedback, req,
    +                         error_from_response=common_header_errors, **kwargs)
     
    +    def docking_command_feedback_full_async(self, command_id, **kwargs):
    +        """Async version of docking_command_feedback_full()."""
    +        req = self._docking_command_feedback_request(command_id)
    +        return self.call_async(self._stub.DockingCommandFeedback, req,
    +                               error_from_response=common_header_errors, **kwargs)
    +
    +    @deprecated(
    +        reason='This function can raise LeaseErrors when the feedback was successfully retrieved. '
    +        'Use docking_command_feedback_full instead.', version='3.0.0', action='always')
         def docking_command_feedback(self, command_id, **kwargs):
             """Check the status of a previously issued docking command.
     
    @@ -72,12 +117,15 @@ def docking_command_feedback(self, command_id, **kwargs):
                 command_id: The ID returned from a previous docking_command call.
     
             Returns:
    -            Status of type DockingCommandResponse.Status
    +            Status of type DockingCommandFeedbackResponse.Status
             """
             req = self._docking_command_feedback_request(command_id)
             return self.call(self._stub.DockingCommandFeedback, req, self._docking_status_from_response,
                              _docking_feedback_error_from_response, **kwargs)
     
    +    @deprecated(
    +        reason='This function can raise LeaseErrors when the feedback was successfully retrieved. '
    +        'Use docking_command_feedback_full_async instead.', version='3.0.0', action='always')
         def docking_command_feedback_async(self, command_id, **kwargs):
             """Async version of docking_command_feedback()."""
             req = self._docking_command_feedback_request(command_id)
    @@ -180,7 +228,7 @@ def _docking_get_state_error_from_response(response):
         return None
     
     
    -def blocking_dock_robot(robot, dock_id, num_retries=4):
    +def blocking_dock_robot(robot, dock_id, num_retries=4, timeout=30):
         """Blocking helper that takes control of the robot and docks it.
     
         Args:
    @@ -202,18 +250,21 @@ def blocking_dock_robot(robot, dock_id, num_retries=4):
         # Try to dock the robot
         while attempt_number < num_retries and not docking_success:
             attempt_number += 1
    -        cmd_end_time = time.time() + 30  # expect to finish in 30 seconds
    +        converter = robot.time_sync.get_robot_time_converter()
    +        start_time = converter.robot_seconds_from_local_seconds(now_sec())
    +        cmd_end_time = start_time + timeout
             cmd_timeout = cmd_end_time + 10  # client side buffer
     
             prep_pose = (docking_pb2.PREP_POSE_USE_POSE if
                          (attempt_number % 2) else docking_pb2.PREP_POSE_SKIP_POSE)
     
    -        cmd_id = docking_client.docking_command(
    -            dock_id, robot.time_sync.endpoint.clock_identifier,
    -            robot.time_sync.robot_timestamp_from_local_secs(cmd_end_time), prep_pose)
    +        cmd_id = docking_client.docking_command(dock_id, robot.time_sync.endpoint.clock_identifier,
    +                                                seconds_to_timestamp(cmd_end_time), prep_pose)
     
    -        while time.time() < cmd_timeout:
    -            status = docking_client.docking_command_feedback(cmd_id)
    +        while converter.robot_seconds_from_local_seconds(now_sec()) < cmd_timeout:
    +            feedback = docking_client.docking_command_feedback_full(cmd_id)
    +            maybe_raise(common_lease_errors(feedback))
    +            status = feedback.status
                 if status == docking_pb2.DockingCommandFeedbackResponse.STATUS_IN_PROGRESS:
                     # keep waiting/trying
                     time.sleep(1)
    @@ -259,17 +310,19 @@ def blocking_go_to_prep_pose(robot, dock_id, timeout=20):
         """
         docking_client = robot.ensure_client(DockingClient.default_service_name)
     
    -    # Try and put the robot in a safe position
    -    cmd_end_time = time.time() + timeout
    +    converter = robot.time_sync.get_robot_time_converter()
    +    start_time = converter.robot_seconds_from_local_seconds(now_sec())
    +    cmd_end_time = start_time + timeout
         cmd_timeout = cmd_end_time + 10  # client side buffer
     
    -    cmd_id = docking_client.docking_command(
    -        dock_id, robot.time_sync.endpoint.clock_identifier,
    -        robot.time_sync.robot_timestamp_from_local_secs(cmd_end_time),
    -        docking_pb2.PREP_POSE_ONLY_POSE)
    +    cmd_id = docking_client.docking_command(dock_id, robot.time_sync.endpoint.clock_identifier,
    +                                            seconds_to_timestamp(cmd_end_time),
    +                                            docking_pb2.PREP_POSE_ONLY_POSE)
     
    -    while time.time() < cmd_timeout:
    -        status = docking_client.docking_command_feedback(cmd_id)
    +    while converter.robot_seconds_from_local_seconds(now_sec()) < cmd_timeout:
    +        feedback = docking_client.docking_command_feedback_full(cmd_id)
    +        maybe_raise(common_lease_errors(feedback))
    +        status = feedback.status
             if status == docking_pb2.DockingCommandFeedbackResponse.STATUS_IN_PROGRESS:
                 # keep waiting/trying
                 time.sleep(1)
    @@ -296,16 +349,19 @@ def blocking_undock(robot, timeout=20):
         """
         docking_client = robot.ensure_client(DockingClient.default_service_name)
     
    -    # Try and put the robot in a safe position
    -    cmd_end_time = time.time() + timeout
    +    converter = robot.time_sync.get_robot_time_converter()
    +    start_time = converter.robot_seconds_from_local_seconds(now_sec())
    +    cmd_end_time = start_time + timeout
         cmd_timeout = cmd_end_time + 10  # client side buffer
     
    -    cmd_id = docking_client.docking_command(
    -        0, robot.time_sync.endpoint.clock_identifier,
    -        robot.time_sync.robot_timestamp_from_local_secs(cmd_end_time), docking_pb2.PREP_POSE_UNDOCK)
    +    cmd_id = docking_client.docking_command(0, robot.time_sync.endpoint.clock_identifier,
    +                                            seconds_to_timestamp(cmd_end_time),
    +                                            docking_pb2.PREP_POSE_UNDOCK)
     
    -    while time.time() < cmd_timeout:
    -        status = docking_client.docking_command_feedback(cmd_id)
    +    while converter.robot_seconds_from_local_seconds(now_sec()) < cmd_timeout:
    +        feedback = docking_client.docking_command_feedback_full(cmd_id)
    +        maybe_raise(common_lease_errors(feedback))
    +        status = feedback.status
             if status == docking_pb2.DockingCommandFeedbackResponse.STATUS_IN_PROGRESS:
                 # keep waiting/trying
                 time.sleep(1)
    diff --git a/python/bosdyn-client/src/bosdyn/client/estop.py b/python/bosdyn-client/src/bosdyn/client/estop.py
    index 7d39aecb6..d1d4ea98d 100644
    --- a/python/bosdyn-client/src/bosdyn/client/estop.py
    +++ b/python/bosdyn-client/src/bosdyn/client/estop.py
    @@ -415,11 +415,10 @@ def to_proto(self):
             else:
                 cpt_seconds = int(self.estop_cut_power_timeout)
                 cpt_nanos = int((self.estop_cut_power_timeout - cpt_seconds) * 1e9)
    -            return estop_pb2.EstopEndpoint(role=self.role, name=self._name,
    -                                           unique_id=self._unique_id,
    -                                           timeout=Duration(seconds=t_seconds, nanos=t_nanos),
    -                                           cut_power_timeout=Duration(seconds=cpt_seconds,
    -                                                                      nanos=cpt_nanos))
    +            return estop_pb2.EstopEndpoint(
    +                role=self.role, name=self._name, unique_id=self._unique_id,
    +                timeout=Duration(seconds=t_seconds, nanos=t_nanos),
    +                cut_power_timeout=Duration(seconds=cpt_seconds, nanos=cpt_nanos))
     
         def _response(self):
             """Generate a response for self._challenge."""
    @@ -576,8 +575,9 @@ def _periodic_check_in(self):
                     self._error('RPC took longer than {:.2f} seconds'.format(self._rpc_timeout),
                                 exception=exc)
                 except RpcError as exc:
    -                self._error('Transport exception during check-in:\n{}\n'
    -                            '    (resuming check-in)'.format(exc), exception=exc)
    +                self._error(
    +                    'Transport exception during check-in:\n{}\n'
    +                    '    (resuming check-in)'.format(exc), exception=exc)
                 except EndpointUnknownError as exc:
                     # Disable ourself to show we cannot estop any longer.
                     self._error(str(exc), exception=exc, disable=True)
    @@ -617,6 +617,7 @@ class KeepAliveStatus(enum.Enum):
             ERROR = 1
             DISABLED = 2
     
    +
     def is_estopped(estop_client, **kwargs):
         """Returns true if robot is estopped, false otherwise.
     
    @@ -626,6 +627,7 @@ def is_estopped(estop_client, **kwargs):
         response = estop_client.get_status(**kwargs)
         return response.stop_level != estop_pb2.ESTOP_LEVEL_NONE
     
    +
     def response_from_challenge(challenge):
         return ctypes.c_ulonglong(~challenge).value
     
    @@ -633,10 +635,10 @@ def response_from_challenge(challenge):
     _CHECK_IN_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
     _CHECK_IN_STATUS_TO_ERROR.update({
         estop_pb2.EstopCheckInResponse.STATUS_OK: (None, None),
    -    estop_pb2.EstopCheckInResponse.STATUS_ENDPOINT_UNKNOWN: (EndpointUnknownError,
    -                                                             EndpointUnknownError.__doc__),
    +    estop_pb2.EstopCheckInResponse.STATUS_ENDPOINT_UNKNOWN:
    +        (EndpointUnknownError, EndpointUnknownError.__doc__),
         estop_pb2.EstopCheckInResponse.STATUS_INCORRECT_CHALLENGE_RESPONSE:
    -    (IncorrectChallengeResponseError, IncorrectChallengeResponseError.__doc__),
    +        (IncorrectChallengeResponseError, IncorrectChallengeResponseError.__doc__),
     })
     
     
    @@ -677,9 +679,11 @@ def _set_config_error_from_response(response):
     _DEREGISTER_ENDPOINT_STATUS_TO_ERROR.update({
         estop_pb2.DeregisterEstopEndpointResponse.STATUS_SUCCESS: (None, None),
         estop_pb2.DeregisterEstopEndpointResponse.STATUS_ENDPOINT_MISMATCH:
    -    (EndpointMismatchError, EndpointMismatchError.__doc__),
    -    estop_pb2.DeregisterEstopEndpointResponse.STATUS_CONFIG_MISMATCH: (ConfigMismatchError,
    -                                                                       ConfigMismatchError.__doc__),
    +        (EndpointMismatchError, EndpointMismatchError.__doc__),
    +    estop_pb2.DeregisterEstopEndpointResponse.STATUS_CONFIG_MISMATCH:
    +        (ConfigMismatchError, ConfigMismatchError.__doc__),
    +    estop_pb2.DeregisterEstopEndpointResponse.STATUS_MOTORS_ON:
    +        (MotorsOnError, MotorsOnError.__doc__),
     })
     
     
    @@ -696,11 +700,11 @@ def _deregister_endpoint_error_from_response(response):
     _REGISTER_ENDPOINT_STATUS_TO_ERROR.update({
         estop_pb2.RegisterEstopEndpointResponse.STATUS_SUCCESS: (None, None),
         estop_pb2.RegisterEstopEndpointResponse.STATUS_ENDPOINT_MISMATCH:
    -    (EndpointMismatchError, EndpointMismatchError.__doc__),
    -    estop_pb2.RegisterEstopEndpointResponse.STATUS_CONFIG_MISMATCH: (ConfigMismatchError,
    -                                                                     ConfigMismatchError.__doc__),
    -    estop_pb2.RegisterEstopEndpointResponse.STATUS_INVALID_ENDPOINT: (InvalidEndpointError,
    -                                                                      InvalidEndpointError.__doc__),
    +        (EndpointMismatchError, EndpointMismatchError.__doc__),
    +    estop_pb2.RegisterEstopEndpointResponse.STATUS_CONFIG_MISMATCH:
    +        (ConfigMismatchError, ConfigMismatchError.__doc__),
    +    estop_pb2.RegisterEstopEndpointResponse.STATUS_INVALID_ENDPOINT:
    +        (InvalidEndpointError, InvalidEndpointError.__doc__),
     })
     
     
    diff --git a/python/bosdyn-client/src/bosdyn/client/exceptions.py b/python/bosdyn-client/src/bosdyn/client/exceptions.py
    index 166bd92f4..3f5402bba 100644
    --- a/python/bosdyn-client/src/bosdyn/client/exceptions.py
    +++ b/python/bosdyn-client/src/bosdyn/client/exceptions.py
    @@ -30,16 +30,12 @@ def __str__(self):
     
     
     class InvalidRequestError(ResponseError):
    -    """The provided request arguments are ill-formed or invalid.
    -
    -       This is programmer error, hence it should be fixed and not ignored.
    -       This error does not depend on the state of the system."""
    +    """The provided request arguments are ill-formed or invalid, independent of the system state."""
     
     
     class LeaseUseError(ResponseError):
    -    """Request was rejected due to using an invalid lease.
    +    """Request was rejected due to using an invalid lease."""
     
    -       This is thrown by services outside of LeaseService."""
     
     class LicenseError(ResponseError):
         """Request was rejected due to using an invalid license."""
    @@ -70,71 +66,83 @@ def __str__(self):
             return '{}: {}'.format(full_classname, self.error_message)
     
     
    -class ClientCancelledOperationError(RpcError):
    +class RetryableRpcError(RpcError):
    +    """An RpcError that denotes the same request may succeed if retried."""
    +
    +
    +class PersistentRpcError(RpcError):
    +    """An RpcError that will almost certainly continue to keep failing if retried"""
    +
    +
    +class ClientCancelledOperationError(PersistentRpcError):
         """The user cancelled the rpc request."""
     
     
    -class InvalidAppTokenError(RpcError):
    +class InvalidAppTokenError(PersistentRpcError):
         """The provided app token is invalid."""
     
     
    -class InvalidClientCertificateError(RpcError):
    +class InvalidClientCertificateError(PersistentRpcError):
         """The provided client certificate is invalid."""
     
     
    -class NonexistentAuthorityError(RpcError):
    +class NonexistentAuthorityError(PersistentRpcError):
         """The app token's authority field names a nonexistent service."""
     
     
    -class PermissionDeniedError(RpcError):
    +class PermissionDeniedError(PersistentRpcError):
         """The rpc request was denied access."""
     
     
    -class ProxyConnectionError(RpcError):
    +class ProxyConnectionError(RetryableRpcError):
         """The proxy on the robot could not be reached."""
     
     
    -class ResponseTooLargeError(RpcError):
    +class ResponseTooLargeError(RetryableRpcError):
         """The rpc response was larger than allowed max size."""
     
     
    -class ServiceUnavailableError(RpcError):
    +class ServiceUnavailableError(RetryableRpcError):
         """The proxy could not find the (possibly unregistered) service."""
     
     
    -class ServiceFailedDuringExecutionError(RpcError):
    +class TooManyRequestsError(RetryableRpcError):
    +    """The remote procedure call did not go through the proxy due to rate limiting."""
    +
    +
    +class ServiceFailedDuringExecutionError(RetryableRpcError):
         """The service encountered an unexpected failure."""
     
     
    -class TimedOutError(RpcError):
    +class TimedOutError(RetryableRpcError):
         """The remote procedure call did not terminate within the allotted time."""
     
     
    -class UnableToConnectToRobotError(RpcError):
    +class UnableToConnectToRobotError(RetryableRpcError):
         """The robot may be offline or otherwise unreachable."""
     
     
     class RetryableUnavailableError(UnableToConnectToRobotError):
    -    """gRPC service unavailable. Likely transient and can be resolved by retrying the request."""
    +    """Service unavailable or channel reset. Likely transient and can be resolved by retrying."""
     
     
    -class UnauthenticatedError(RpcError):
    +class UnauthenticatedError(PersistentRpcError):
         """The user needs to authenticate or does not have permission to access requested service."""
     
     
    -class UnknownDnsNameError(RpcError):
    +class UnknownDnsNameError(PersistentRpcError):
         """The system is unable to translate the domain name."""
     
     
    -class NotFoundError(RpcError):
    +class NotFoundError(PersistentRpcError):
         """The backend system could not be found."""
     
     
    -class UnimplementedError(RpcError):
    +class UnimplementedError(PersistentRpcError):
         """The API does not recognize the request and is unable to complete the request."""
     
     
    -class TransientFailureError(RpcError):
    +class TransientFailureError(RetryableRpcError):
         """The channel is in state TRANSIENT_FAILURE, often caused by a connection failure."""
     
     
    diff --git a/python/bosdyn-client/src/bosdyn/client/frame_helpers.py b/python/bosdyn-client/src/bosdyn/client/frame_helpers.py
    index d101a8d82..e35217c74 100644
    --- a/python/bosdyn-client/src/bosdyn/client/frame_helpers.py
    +++ b/python/bosdyn-client/src/bosdyn/client/frame_helpers.py
    @@ -178,7 +178,8 @@ def get_se2_a_tform_b(frame_tree_snapshot, frame_a, frame_b, validate=True):
         return se3_a_tform_b.get_closest_se2_transform()
     
     
    -def express_se2_velocity_in_new_frame(frame_tree_snapshot, frame_b, frame_c, vel_of_a_in_b, validate=True):
    +def express_se2_velocity_in_new_frame(frame_tree_snapshot, frame_b, frame_c, vel_of_a_in_b,
    +                                      validate=True):
         """Convert the SE2 Velocity in frame b to a SE2 Velocity in frame c using
            the frame tree snapshot.
     
    @@ -210,7 +211,9 @@ def express_se2_velocity_in_new_frame(frame_tree_snapshot, frame_b, frame_c, vel
         vel_of_a_in_c = math_helpers.transform_se2velocity(c_adjoint_b, vel_of_a_in_b)
         return vel_of_a_in_c
     
    -def express_se3_velocity_in_new_frame(frame_tree_snapshot, frame_b, frame_c, vel_of_a_in_b, validate=True):
    +
    +def express_se3_velocity_in_new_frame(frame_tree_snapshot, frame_b, frame_c, vel_of_a_in_b,
    +                                      validate=True):
         """Convert the SE(3) Velocity in frame b to an SE(3) Velocity in frame c using
            the frame tree snapshot.
     
    @@ -235,6 +238,7 @@ def express_se3_velocity_in_new_frame(frame_tree_snapshot, frame_b, frame_c, vel
         vel_of_a_in_c = math_helpers.transform_se3velocity(c_adjoint_b, vel_of_a_in_b)
         return vel_of_a_in_c
     
    +
     def get_odom_tform_body(frame_tree_snapshot):
         """Get the transformation between "odom" frame and "body" frame from the FrameTreeSnapshot."""
         return get_a_tform_b(frame_tree_snapshot, ODOM_FRAME_NAME, BODY_FRAME_NAME)
    diff --git a/python/bosdyn-client/src/bosdyn/client/graph_nav.py b/python/bosdyn-client/src/bosdyn/client/graph_nav.py
    index f9284ed9f..d59e86379 100644
    --- a/python/bosdyn-client/src/bosdyn/client/graph_nav.py
    +++ b/python/bosdyn-client/src/bosdyn/client/graph_nav.py
    @@ -9,6 +9,7 @@
     import collections
     import math
     import os
    +from deprecated import deprecated
     from bosdyn.api.graph_nav import graph_nav_service_pb2_grpc
     from bosdyn.api.graph_nav import graph_nav_service_pb2
     from bosdyn.api.graph_nav import graph_nav_pb2
    @@ -89,14 +90,15 @@ def set_localization_async(
             return self.call_async(self._stub.SetLocalization, req, _localization_from_response,
                                    _set_localization_error, **kwargs)
     
    -    def get_localization_state(self,
    -                               request_live_point_cloud=False,
    -                               request_live_images=False,
    -                               request_live_terrain_maps=False,
    -                               request_live_world_objects=False,
    -                               request_live_robot_state=False,
    -                               waypoint_id=None,
    -                               **kwargs):
    +    def get_localization_state(
    +            self,
    +            request_live_point_cloud=False,
    +            request_live_images=False,
    +            request_live_terrain_maps=False,
    +            request_live_world_objects=False,
    +            request_live_robot_state=False,
    +            waypoint_id=None,
    +            **kwargs):
             """Obtain current localization state of the robot.
     
             Returns:
    @@ -109,8 +111,7 @@ def get_localization_state(self,
                 request_live_images=request_live_images,
                 request_live_terrain_maps=request_live_terrain_maps,
                 request_live_world_objects=request_live_world_objects,
    -            request_live_robot_state=request_live_robot_state,
    -            waypoint_id=waypoint_id)
    +            request_live_robot_state=request_live_robot_state, waypoint_id=waypoint_id)
             return self.call(self._stub.GetLocalizationState, req, None, common_header_errors, **kwargs)
     
         def get_localization_state_async(self, request_live_point_cloud=False,
    @@ -127,18 +128,22 @@ def get_localization_state_async(self, request_live_point_cloud=False,
             return self.call_async(self._stub.GetLocalizationState, req, None, common_header_errors,
                                    **kwargs)
     
    -    def navigate_route(self, route, cmd_duration, travel_params=None, leases=None,
    -                       timesync_endpoint=None, command_id=None, **kwargs):
    +    def navigate_route(self, route, cmd_duration, route_follow_params=None, travel_params=None,
    +                       leases=None, timesync_endpoint=None, command_id=None,
    +                       destination_waypoint_tform_body_goal=None, **kwargs):
             """Navigate the given route.
     
             Args:
                 route: Route protobuf of the route to follow.
    +            route_follow_params: What should the robot do if it is not at the expected point in the
    +            route, or the route is blocked.
                 travel_params: API TravelParams for the route.
                 cmd_duration: Number of seconds the command can run for.
                 leases: Leases to show ownership of necessary resources. Will use the client's leases by default.
                 timesync_endpoint: Use this endpoint for timesync fields. Will use the client's endpoint by default.
                 command_id: If not None, this continues an existing navigate_route command with the given ID. If None,
                 a new command_id will be used.
    +            destination_waypoint_tform_body_goal: SE2Pose protobuf of an offset relative to the destination waypoint.
                 kwargs: Passed to underlying RPC. Example: timeout=5 to cancel the RPC after 5 seconds.
             Returns:
                 Command ID to use in feedback lookup.
    @@ -150,8 +155,9 @@ def navigate_route(self, route, cmd_duration, travel_params=None, leases=None,
                 graph_nav.TooDistantError: Time too far in the future.
                 graph_nav.RobotImpairedError: Robot cannot travel a route.
                 graph_nav.IsRecordingError: Robot cannot navigate while recording.
    -            graph_nav.UnkownRouteElementsError: Unknown edges or waypoints
    +            graph_nav.UnknownRouteElementsError: Unknown edges or waypoints
                 graph_nav.InvalidEdgeError: Mismatch between edges and waypoints.
    +            graph_nav.NoPathError: No path to the specified route.
                 graph_nav.RobotNotLocalizedToRouteError: The robot is localized somewhere else.
                 graph_nav.ConstraintFaultError: The route involves invalid constraints.
                 graph_nav.RouteNavigationError: A subclass detailing trouble navigating the route.
    @@ -159,23 +165,56 @@ def navigate_route(self, route, cmd_duration, travel_params=None, leases=None,
             used_endpoint = timesync_endpoint or self._timesync_endpoint
             if not used_endpoint:
                 raise GraphNavServiceResponseError(response=None, error_message='No timesync endpoint!')
    -        request = self._build_navigate_route_request(route, travel_params, cmd_duration, leases,
    -                                                     used_endpoint, command_id)
    +        request = self._build_navigate_route_request(route, route_follow_params, travel_params,
    +                                                     cmd_duration, leases, used_endpoint,
    +                                                     command_id,
    +                                                     destination_waypoint_tform_body_goal)
             return self.call(self._stub.NavigateRoute, request,
                              _command_id_from_navigate_route_response, _navigate_route_error, **kwargs)
     
    -    def navigate_route_async(self, route, cmd_duration, travel_params=None, leases=None,
    -                             timesync_endpoint=None, command_id=None, **kwargs):
    +    def navigate_route_async(self, route, cmd_duration, route_follow_params=None,
    +                             travel_params=None, leases=None, timesync_endpoint=None,
    +                             command_id=None, destination_waypoint_tform_body_goal=None, **kwargs):
             """Async version of navigate_route()"""
             used_endpoint = timesync_endpoint or self._timesync_endpoint
             if not used_endpoint:
                 raise GraphNavServiceResponseError(response=None, error_message='No timesync endpoint!')
    -        request = self._build_navigate_route_request(route, travel_params, cmd_duration, leases,
    -                                                     used_endpoint, command_id)
    +        request = self._build_navigate_route_request(route, route_follow_params, travel_params,
    +                                                     cmd_duration, leases, used_endpoint,
    +                                                     command_id,
    +                                                     destination_waypoint_tform_body_goal)
             return self.call_async(self._stub.NavigateRoute, request,
                                    _command_id_from_navigate_route_response, _navigate_route_error,
                                    **kwargs)
     
    +    def navigate_route_full(self, route, route_follow_params, cmd_duration, travel_params=None,
    +                            leases=None, timesync_endpoint=None, command_id=None,
    +                            destination_waypoint_tform_body_goal=None, **kwargs):
    +        """Identical to navigate_route(), except will return the full NavigateRouteResponse."""
    +        used_endpoint = timesync_endpoint or self._timesync_endpoint
    +        if not used_endpoint:
    +            raise GraphNavServiceResponseError(response=None, error_message='No timesync endpoint!')
    +        request = self._build_navigate_route_request(route, route_follow_params, travel_params,
    +                                                     cmd_duration, leases, used_endpoint, command_id,
    +                                                     destination_waypoint_tform_body_goal)
    +        return self.call(self._stub.NavigateRoute, request,
    +                         error_from_response=_navigate_route_error, **kwargs)
    +
    +    def navigate_route_full_async(self, route, cmd_duration, route_follow_params=None,
    +                                  travel_params=None, leases=None, timesync_endpoint=None,
    +                                  command_id=None, destination_waypoint_tform_body_goal=None,
    +                                  **kwargs):
    +        """Async version of navigate_route_full()."""
    +        used_endpoint = timesync_endpoint or self._timesync_endpoint
    +        if not used_endpoint:
    +            raise GraphNavServiceResponseError(response=None, error_message='No timesync endpoint!')
    +        request = self._build_navigate_route_request(route, route_follow_params, travel_params,
    +                                                     cmd_duration, leases, used_endpoint,
    +                                                     command_id,
    +                                                     destination_waypoint_tform_body_goal)
    +        return self.call_async(self._stub.NavigateRoute, request,
    +                               error_from_response=_navigate_route_error, **kwargs)
    +
         def navigate_to(self, destination_waypoint_id, cmd_duration, route_params=None,
                         travel_params=None, leases=None, timesync_endpoint=None, command_id=None,
                         destination_waypoint_tform_body_goal=None, **kwargs):
    @@ -210,8 +249,8 @@ def navigate_to(self, destination_waypoint_id, cmd_duration, route_params=None,
             if not used_endpoint:
                 raise GraphNavServiceResponseError(response=None, error_message='No timesync endpoint!')
             request = self._build_navigate_to_request(destination_waypoint_id, travel_params,
    -                                                  route_params, cmd_duration, leases, used_endpoint, command_id,
    -                                                  destination_waypoint_tform_body_goal)
    +                                                  route_params, cmd_duration, leases, used_endpoint,
    +                                                  command_id, destination_waypoint_tform_body_goal)
             return self.call(self._stub.NavigateTo, request,
                              value_from_response=_command_id_from_navigate_route_response,
                              error_from_response=_navigate_to_error, **kwargs)
    @@ -224,12 +263,97 @@ def navigate_to_async(self, destination_waypoint_id, cmd_duration, route_params=
             if not used_endpoint:
                 raise GraphNavServiceResponseError(response=None, error_message='No timesync endpoint!')
             request = self._build_navigate_to_request(destination_waypoint_id, travel_params,
    -                                                  route_params, cmd_duration, leases, used_endpoint, command_id,
    -                                                  destination_waypoint_tform_body_goal)
    +                                                  route_params, cmd_duration, leases, used_endpoint,
    +                                                  command_id, destination_waypoint_tform_body_goal)
             return self.call_async(self._stub.NavigateTo, request,
                                    value_from_response=_command_id_from_navigate_route_response,
                                    error_from_response=_navigate_to_error, **kwargs)
     
    +    def navigate_to_full(self, destination_waypoint_id, cmd_duration, route_params=None,
    +                    travel_params=None, leases=None, timesync_endpoint=None, command_id=None,
    +                    destination_waypoint_tform_body_goal=None, **kwargs):
    +        """Identical to navigate_to(), except will return the full NavigateToResponse."""
    +        used_endpoint = timesync_endpoint or self._timesync_endpoint
    +        if not used_endpoint:
    +            raise GraphNavServiceResponseError(response=None, error_message='No timesync endpoint!')
    +        request = self._build_navigate_to_request(destination_waypoint_id, travel_params,
    +                                                  route_params, cmd_duration, leases, used_endpoint,
    +                                                  command_id, destination_waypoint_tform_body_goal)
    +        return self.call(self._stub.NavigateTo, request,
    +                         error_from_response=_navigate_to_error, **kwargs)
    +
    +    def navigate_to_full_async(self, destination_waypoint_id, cmd_duration, route_params=None,
    +                          travel_params=None, leases=None, timesync_endpoint=None, command_id=None,
    +                          destination_waypoint_tform_body_goal=None, **kwargs):
    +        """Async version of navigate_to_full()."""
    +        used_endpoint = timesync_endpoint or self._timesync_endpoint
    +        if not used_endpoint:
    +            raise GraphNavServiceResponseError(response=None, error_message='No timesync endpoint!')
    +        request = self._build_navigate_to_request(destination_waypoint_id, travel_params,
    +                                                  route_params, cmd_duration, leases, used_endpoint,
    +                                                  command_id, destination_waypoint_tform_body_goal)
    +        return self.call_async(self._stub.NavigateTo, request,
    +                               error_from_response=_navigate_to_error, **kwargs)
    +
    +    def navigate_to_anchor(self, seed_tform_goal, cmd_duration, route_params=None,
    +                           travel_params=None, leases=None, timesync_endpoint=None,
    +                           goal_waypoint_rt_seed_ewrt_seed_tolerance=None, command_id=None,
    +                           **kwargs):
    +        """Navigate to a pose in seed frame along a route chosen by the GraphNav service.
    +
    +        Args:
    +            seed_tform_goal: SE3Pose protobuf of the goal pose in seed frame.
    +            cmd_duration: Number of seconds the command can run for.
    +            route_params: API RouteGenParams for the route.
    +            travel_params: API TravelParams for the route.
    +            leases: Leases to show ownership of necessary resources. Will use the client's leases by default.
    +            timesync_endpoint: Use this endpoint for timesync fields. Will use the client's endpoint by default.
    +            goal_waypoint_rt_seed_ewrt_seed_tolerance: Vec3 protobuf of the tolerances for goal waypoint selection.
    +            command_id: If not None, this continues an existing navigate_to command with the given ID. If None,
    +            a new command_id will be used.
    +        Returns:
    +            int: Command ID to use in feedback lookup.
    +        Raises:
    +            RpcError: Problem communicating with the robot.
    +            LeaseUseError: Error using provided leases.
    +            graph_nav.NoTimeSyncError: Missing clock identifier.
    +            graph_nav.CommandExpiredError: Command already expired.
    +            graph_nav.TooDistantError: Time too far in the future.
    +            graph_nav.RobotImpairedError: Robot cannot travel a route.
    +            graph_nav.IsRecordingError: Robot cannot navigate while recording.
    +            graph_nav.NoAnchoringError: There is no anchoring.
    +            graph_nav.NoPathError: No route to goal waypoint, or no goal waypoint found.
    +            graph_nav.InvalidPoseError: The requested pose is invalid, or known to be unachievable.
    +            graph_nav.RobotNotLocalizedToRouteError: The robot not correctly localized.
    +            graph_nav.RouteNavigationError: A subclass detailing trouble navigating the route.
    +        """
    +        used_endpoint = timesync_endpoint or self._timesync_endpoint
    +        if not used_endpoint:
    +            raise GraphNavServiceResponseError(response=None, error_message='No timesync endpoint!')
    +        request = self._build_navigate_to_anchor_request(seed_tform_goal, travel_params,
    +                                                         route_params, cmd_duration, leases,
    +                                                         used_endpoint, command_id,
    +                                                         goal_waypoint_rt_seed_ewrt_seed_tolerance)
    +        return self.call(self._stub.NavigateToAnchor, request,
    +                         value_from_response=_command_id_from_navigate_route_response,
    +                         error_from_response=_navigate_to_anchor_error, **kwargs)
    +
    +    def navigate_to_anchor_async(self, seed_tform_goal, cmd_duration, route_params=None,
    +                                 travel_params=None, leases=None, timesync_endpoint=None,
    +                                 goal_waypoint_rt_seed_ewrt_seed_tolerance=None, command_id=None,
    +                                 **kwargs):
    +        """Async version of navigate_to_anchor()."""
    +        used_endpoint = timesync_endpoint or self._timesync_endpoint
    +        if not used_endpoint:
    +            raise GraphNavServiceResponseError(response=None, error_message='No timesync endpoint!')
    +        request = self._build_navigate_to_anchor_request(seed_tform_goal, travel_params,
    +                                                         route_params, cmd_duration, leases,
    +                                                         used_endpoint, command_id,
    +                                                         goal_waypoint_rt_seed_ewrt_seed_tolerance)
    +        return self.call_async(self._stub.NavigateTo, request,
    +                               value_from_response=_command_id_from_navigate_route_response,
    +                               error_from_response=_navigate_to_anchor_error, **kwargs)
    +
         def navigation_feedback(self, command_id=0, **kwargs):
             """Returns the feedback corresponding to the active route follow command.
     
    @@ -263,35 +387,38 @@ def clear_graph(self, lease=None, **kwargs):
             """
             request = self._build_clear_graph_request(lease)
             return self.call(self._stub.ClearGraph, request, value_from_response=None,
    -                         error_from_response=handle_common_header_errors(common_lease_errors), **kwargs)
    +                         error_from_response=handle_common_header_errors(common_lease_errors),
    +                         **kwargs)
     
         def clear_graph_async(self, lease=None, **kwargs):
             """Async version of clear_graph()."""
             request = self._build_clear_graph_request(lease)
             return self.call_async(self._stub.ClearGraph, request, value_from_response=None,
    -                               error_from_response=handle_common_header_errors(common_lease_errors), **kwargs)
    +                               error_from_response=handle_common_header_errors(common_lease_errors),
    +                               **kwargs)
     
    -    def upload_graph(self, lease=None, graph=None, **kwargs):
    +    def upload_graph(self, lease=None, graph=None, generate_new_anchoring=False, **kwargs):
             """Uploads a graph to the server and appends to the existing graph.
     
             Args:
                 leases: Leases to show ownership of necessary resources. Will use the client's leases by default.
                 graph: Graph protobuf that represents the map with waypoints and edges.
    +            generate_new_anchoring: Whether to generate an (overwrite the) anchoring on upload.
             Returns:
                 The response, which includes waypoint and edge id's sorted by whether it was cached.
             Raises:
                 RpcError: Problem communicating with the robot.
                 LeaseUseError: Error using provided lease.
             """
    -        request = self._build_upload_graph_request(lease, graph)
    +        request = self._build_upload_graph_request(lease, graph, generate_new_anchoring)
             return self.call(self._stub.UploadGraph, request, value_from_response=_get_response,
    -                         error_from_response=handle_common_header_errors(common_lease_errors), **kwargs)
    +                         error_from_response=_upload_graph_error, **kwargs)
     
    -    def upload_graph_async(self, lease=None, graph=None, **kwargs):
    +    def upload_graph_async(self, lease=None, graph=None, generate_new_anchoring=False, **kwargs):
             """Async version of upload_graph()."""
    -        request = self._build_upload_graph_request(lease, graph)
    +        request = self._build_upload_graph_request(lease, graph, generate_new_anchoring)
             return self.call_async(self._stub.UploadGraph, request, value_from_response=_get_response,
    -                               error_from_response=handle_common_header_errors(common_lease_errors), **kwargs)
    +                               error_from_response=_upload_graph_error, **kwargs)
     
         def upload_waypoint_snapshot(self, waypoint_snapshot, lease=None, **kwargs):
             """Uploads large waypoint snapshot as a stream for a particular waypoint.
    @@ -306,10 +433,11 @@ def upload_waypoint_snapshot(self, waypoint_snapshot, lease=None, **kwargs):
                 LeaseUseError: Error using provided lease.
             """
             serialized = waypoint_snapshot.SerializeToString()
    -        self.call(self._stub.UploadWaypointSnapshot,
    -                  GraphNavClient._data_chunk_iterator_upload_waypoint_snapshot(
    -                      serialized, lease, self._data_chunk_size), value_from_response=None,
    -                  error_from_response=handle_common_header_errors(common_lease_errors), **kwargs)
    +        self.call(
    +            self._stub.UploadWaypointSnapshot,
    +            GraphNavClient._data_chunk_iterator_upload_waypoint_snapshot(
    +                serialized, lease, self._data_chunk_size), value_from_response=None,
    +            error_from_response=handle_common_header_errors(common_lease_errors), **kwargs)
     
         def upload_edge_snapshot(self, edge_snapshot, lease=None, **kwargs):
             """Uploads large edge snapshot as a stream for a particular edge.
    @@ -324,10 +452,12 @@ def upload_edge_snapshot(self, edge_snapshot, lease=None, **kwargs):
                 LeaseUseError: Error using provided leases.
             """
             serialized = edge_snapshot.SerializeToString()
    -        self.call(self._stub.UploadEdgeSnapshot,
    -                  GraphNavClient._data_chunk_iterator_upload_edge_snapshot(
    -                      serialized, lease, self._data_chunk_size), value_from_response=None,
    -                  error_from_response=handle_common_header_errors(common_lease_errors), **kwargs)
    +        self.call(
    +            self._stub.UploadEdgeSnapshot,
    +            GraphNavClient._data_chunk_iterator_upload_edge_snapshot(serialized, lease,
    +                                                                     self._data_chunk_size),
    +            value_from_response=None,
    +            error_from_response=handle_common_header_errors(common_lease_errors), **kwargs)
     
         def download_graph(self, **kwargs):
             """Downloads the graph from the server.
    @@ -347,28 +477,33 @@ def download_graph_async(self, **kwargs):
             return self.call_async(self._stub.DownloadGraph, request, value_from_response=_get_graph,
                                    error_from_response=common_header_errors, **kwargs)
     
    -    def download_waypoint_snapshot(self,
    -                                    waypoint_snapshot_id,
    -                                    download_images=False,
    -                                    **kwargs):
    +    def download_waypoint_snapshot(
    +            self,
    +            waypoint_snapshot_id,
    +            download_images=False,
    +            do_not_download_point_cloud=False,
    +            **kwargs):
             """Download a specific waypoint snapshot with streaming from the server.
     
             Args:
                 waypoint_snapshot_id: WaypointSnapshot string ID for which snapshot to download from robot.
                 download_images: Boolean indicating whether or not to include images in the download.
    +            do_not_download_point_cloud: Boolean indicating if point cloud data should not be downloaded.
             Returns:
                 The WaypointSnapshot protobuf from the robot's current map.
             Raises:
                 RpcError: Problem communicating with the robot
                 UnknownMapInformationError: Snapshot id not found
             """
    -        request = self._build_download_waypoint_snapshot_request(waypoint_snapshot_id,
    -                                                                 download_images
    -                                                                )
    +        request = self._build_download_waypoint_snapshot_request(
    +            waypoint_snapshot_id,
    +            download_images,
    +            do_not_download_point_cloud)
             return self.call(self._stub.DownloadWaypointSnapshot, request,
                              value_from_response=_get_streamed_waypoint_snapshot,
                              error_from_response=_download_waypoint_snapshot_stream_errors, **kwargs)
     
    +
         def download_edge_snapshot(self, edge_snapshot_id, **kwargs):
             """Downloads a specific edge snapshot with streaming from the server.
     
    @@ -399,11 +534,15 @@ def write_graph_and_snapshots(self, directory):
             self._write_bytes(directory, '/graph', graph_bytes)
     
             for waypoint in graph.waypoints:
    +            if len(waypoint.snapshot_id) == 0:
    +                continue
                 waypoint_snapshot = self.download_waypoint_snapshot(waypoint.snapshot_id)
                 self._write_bytes(directory + '/waypoint_snapshots', '/' + waypoint.snapshot_id,
                                   waypoint_snapshot.SerializeToString())
     
             for edge in graph.edges:
    +            if len(edge.snapshot_id) == 0:
    +                continue
                 edge_snapshot = self.download_edge_snapshot(edge.snapshot_id)
                 self._write_bytes(directory + '/edge_snapshots', '/' + edge.snapshot_id,
                                   edge_snapshot.SerializeToString())
    @@ -432,24 +571,25 @@ def _build_set_localization_request(
             return request
     
         @staticmethod
    -    def _build_get_localization_state_request(request_live_point_cloud,
    -                                              request_live_images, request_live_terrain_maps,
    -                                              request_live_world_objects, request_live_robot_state,
    -                                              waypoint_id):
    +    def _build_get_localization_state_request(request_live_point_cloud, request_live_images,
    +                                              request_live_terrain_maps, request_live_world_objects,
    +                                              request_live_robot_state, waypoint_id):
             return graph_nav_pb2.GetLocalizationStateRequest(
                 request_live_point_cloud=request_live_point_cloud,
                 request_live_images=request_live_images,
                 request_live_terrain_maps=request_live_terrain_maps,
                 request_live_world_objects=request_live_world_objects,
    -            request_live_robot_state=request_live_robot_state,
    -            waypoint_id=waypoint_id)
    +            request_live_robot_state=request_live_robot_state, waypoint_id=waypoint_id)
     
         @staticmethod
    -    def _build_navigate_route_request(route, travel_params, end_time_secs, leases,
    -                                      timesync_endpoint, command_id):
    +    def _build_navigate_route_request(route, route_follow_params, travel_params, end_time_secs,
    +                                      leases, timesync_endpoint, command_id,
    +                                      destination_waypoint_tform_body_goal):
             converter = timesync_endpoint.get_robot_time_converter()
             request = graph_nav_pb2.NavigateRouteRequest(
    -            route=route, clock_identifier=timesync_endpoint.clock_identifier)
    +            route=route, route_follow_params=route_follow_params,
    +            destination_waypoint_tform_body_goal=destination_waypoint_tform_body_goal,
    +            clock_identifier=timesync_endpoint.clock_identifier)
             if travel_params is not None:
                 request.travel_params.CopyFrom(travel_params)
             request.end_time.CopyFrom(
    @@ -477,6 +617,25 @@ def _build_navigate_to_request(destination_waypoint_id, travel_params, route_par
                 request.command_id = command_id
             return request
     
    +    @staticmethod
    +    def _build_navigate_to_anchor_request(seed_tform_goal, travel_params, route_params,
    +                                          end_time_secs, leases, timesync_endpoint, command_id,
    +                                          goal_waypoint_rt_seed_ewrt_seed_tolerance):
    +        converter = timesync_endpoint.get_robot_time_converter()
    +        request = graph_nav_pb2.NavigateToAnchorRequest(
    +            seed_tform_goal=seed_tform_goal,
    +            goal_waypoint_rt_seed_ewrt_seed_tolerance=goal_waypoint_rt_seed_ewrt_seed_tolerance,
    +            clock_identifier=timesync_endpoint.clock_identifier)
    +        request.end_time.CopyFrom(
    +            converter.robot_timestamp_from_local_secs(time.time() + end_time_secs))
    +        if travel_params is not None:
    +            request.travel_params.CopyFrom(travel_params)
    +        if route_params is not None:
    +            request.route_params.CopyFrom(route_params)
    +        if command_id is not None:
    +            request.command_id = command_id
    +        return request
    +
         @staticmethod
         def _build_clear_graph_request(lease):
             return graph_nav_pb2.ClearGraphRequest(lease=lease)
    @@ -486,8 +645,9 @@ def _build_navigate_feedback_request(command_id=0):
             return graph_nav_pb2.NavigationFeedbackRequest(command_id=command_id)
     
         @staticmethod
    -    def _build_upload_graph_request(lease, graph):
    -        return graph_nav_pb2.UploadGraphRequest(lease=lease, graph=graph)
    +    def _build_upload_graph_request(lease, graph, generate_new_anchoring):
    +        return graph_nav_pb2.UploadGraphRequest(lease=lease, graph=graph,
    +                                                generate_new_anchoring=generate_new_anchoring)
     
         @staticmethod
         def _data_chunk_iterator_upload_waypoint_snapshot(serialized_waypoint_snapshot, lease,
    @@ -526,11 +686,16 @@ def _build_download_graph_request():
             return graph_nav_pb2.DownloadGraphRequest()
     
         @staticmethod
    -    def _build_download_waypoint_snapshot_request(waypoint_snapshot_id, download_images
    -                                                  ):
    +    def _build_download_waypoint_snapshot_request(
    +            waypoint_snapshot_id,
    +            download_images,
    +        do_not_download_point_cloud=False
    +    ):
             return graph_nav_pb2.DownloadWaypointSnapshotRequest(
    -            waypoint_snapshot_id=waypoint_snapshot_id, download_images=download_images
    -            )
    +            waypoint_snapshot_id=waypoint_snapshot_id,
    +            download_images=download_images,
    +            do_not_download_point_cloud=do_not_download_point_cloud
    +        )
     
         @staticmethod
         def _build_download_edge_snapshot_request(edge_snapshot_id):
    @@ -591,6 +756,18 @@ class GraphNavServiceResponseError(ResponseError):
         """General class of errors for the GraphNav Recording Service."""
     
     
    +class UploadGraphError(GraphNavServiceResponseError):
    +    """Errors related to uploading a graph."""
    +
    +
    +class MapTooLargeLicenseError(UploadGraphError):
    +    """The map is too large for the license on the robot."""
    +
    +
    +class InvalidGraphError(UploadGraphError):
    +    """The graph is invalid topologically, e.g. missing waypoints referenced by edges."""
    +
    +
     class RequestAbortedError(GraphNavServiceResponseError):
         """Request was aborted by the system."""
     
    @@ -609,51 +786,97 @@ class UnknownMapInformationError(GraphNavServiceResponseError):
     
     class TimeError(GraphNavServiceResponseError):
         """Errors associated with timestamps and time sync."""
    +
    +
     class CommandExpiredError(TimeError):
         """The command was received after its end time had already passed."""
    +
    +
     class NoTimeSyncError(TimeError):
         """Client has not performed timesync with robot."""
    +
    +
     class TooDistantError(TimeError):
         """The command was too far in the future."""
     
     
     class RobotStateError(GraphNavServiceResponseError):
         """Errors associated with the current state of the robot."""
    +
    +
     class IsRecordingError(RobotStateError):
         """Cannot navigate a route while recording a map."""
    +
    +
     class RobotImpairedError(RobotStateError):
         """Robot has a critical perception or behavior fault and cannot navigate."""
     
     
     class RouteError(GraphNavServiceResponseError):
         """Errors associated with the specified route."""
    +
    +
     class ConstraintFaultError(RouteError):
         """Route parameters contained a constraint fault."""
    +
    +
     class InvalidEdgeError(RouteError):
         """One or more edges do not connect to expected waypoints."""
    +
    +
    +@deprecated(reason='Use UnknownRouteElementsError instead', version='3.0.0', action='ignore')
     class UnkownRouteElementsError(RouteError):
         """One or more waypoints/edges are not in the map."""
    +
    +
    +class UnknownRouteElementsError(UnkownRouteElementsError):
    +    """One or more waypoints/edges are not in the map."""
    +
    +
     class NoPathError(RouteError):
         """There is no path to the specified waypoint."""
    +
    +
     class UnknownWaypointError(RouteError):
         """One or more waypoints are not in the map."""
     
     
    +class NoAnchoringError(RouteError):
    +    """There is no anchoring."""
    +
    +
    +class InvalidPoseError(RouteError):
    +    """The requested pose is invalid, or known to be unachievable."""
    +
    +
     class RouteNavigationError(GraphNavServiceResponseError):
         """Errors related to how the robot navigates the route."""
    +
    +
     class FeatureDesertError(RouteNavigationError):
         """Route contained too many waypoints with low-quality features."""
    +
    +
     class RouteNotUpdatingError(RouteNavigationError):
         """Graph nav was unable to update and follow the specified route."""
    +
    +
     class RobotLostError(RouteNavigationError):
         """Cannot issue a navigation request when the robot is already lost."""
    +
    +
     class RobotNotLocalizedToRouteError(RouteNavigationError):
         """The current localization doesn't refer to any waypoint in the route (possibly uninitialized localization)."""
    +
    +
     class RobotStuckError(RouteNavigationError):
         """The robot is stuck or unable to find a way forward. Resend the command with a new ID, or send a different command to try again."""
    +
    +
     class UnrecongizedCommandError(RouteNavigationError):
         """Happens when you try to continue a command that was either expired, or had an unrecognized id."""
     
    +
     def _localization_from_response(response):
         """Return the localization state from the response."""
         return response.localization
    @@ -711,6 +934,24 @@ def _get_streamed_edge_snapshot(response):
         return edge_snapshot
     
     
    +_UPLOAD_GRAPH_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
    +_UPLOAD_GRAPH_STATUS_TO_ERROR.update({
    +    graph_nav_pb2.UploadGraphResponse.STATUS_OK: (None, None),
    +    graph_nav_pb2.UploadGraphResponse.STATUS_MAP_TOO_LARGE_LICENSE: error_pair(MapTooLargeLicenseError),
    +    graph_nav_pb2.UploadGraphResponse.STATUS_INVALID_GRAPH: error_pair(InvalidGraphError),
    +})
    +
    +
    +@handle_common_header_errors
    +@handle_lease_use_result_errors
    +@handle_unset_status_error(unset='STATUS_UNKNOWN')
    +def _upload_graph_error(response):
    +    """Return a custom exception based on upload graph response, None if no error."""
    +    return error_factory(response, response.status,
    +                         status_to_string=graph_nav_pb2.UploadGraphResponse.Status.Name,
    +                         status_to_error=_UPLOAD_GRAPH_STATUS_TO_ERROR)
    +
    +
     _SET_LOCALIZATION_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
     _SET_LOCALIZATION_STATUS_TO_ERROR.update({
         graph_nav_pb2.SetLocalizationResponse.STATUS_OK: (None, None),
    @@ -747,9 +988,11 @@ def _set_localization_error(response):
         graph_nav_pb2.NavigateRouteResponse.STATUS_RECORDING:
             error_pair(IsRecordingError),
         graph_nav_pb2.NavigateRouteResponse.STATUS_UNKNOWN_ROUTE_ELEMENTS:
    -        error_pair(UnkownRouteElementsError),
    +        error_pair(UnknownRouteElementsError),
         graph_nav_pb2.NavigateRouteResponse.STATUS_INVALID_EDGE:
             error_pair(InvalidEdgeError),
    +    graph_nav_pb2.NavigateRouteResponse.STATUS_NO_PATH:
    +        error_pair(NoPathError),
         graph_nav_pb2.NavigateRouteResponse.STATUS_CONSTRAINT_FAULT:
             error_pair(ConstraintFaultError),
         graph_nav_pb2.NavigateRouteResponse.STATUS_FEATURE_DESERT:
    @@ -758,6 +1001,8 @@ def _set_localization_error(response):
             error_pair(RobotLostError),
         graph_nav_pb2.NavigateRouteResponse.STATUS_NOT_LOCALIZED_TO_ROUTE:
             error_pair(RobotNotLocalizedToRouteError),
    +    graph_nav_pb2.NavigateRouteResponse.STATUS_NOT_LOCALIZED_TO_MAP:
    +        error_pair(RobotNotLocalizedToRouteError),
         graph_nav_pb2.NavigateRouteResponse.STATUS_COULD_NOT_UPDATE_ROUTE:
             error_pair(RouteNotUpdatingError),
         graph_nav_pb2.NavigateRouteResponse.STATUS_STUCK:
    @@ -817,6 +1062,48 @@ def _navigate_to_error(response):
                              status_to_error=_NAVIGATE_TO_STATUS_TO_ERROR)
     
     
    +_NAVIGATE_TO_ANCHOR_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
    +_NAVIGATE_TO_ANCHOR_STATUS_TO_ERROR.update({
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_OK: (None, None),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_NO_TIMESYNC:
    +        error_pair(NoTimeSyncError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_EXPIRED:
    +        error_pair(CommandExpiredError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_TOO_DISTANT:
    +        error_pair(TooDistantError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_ROBOT_IMPAIRED:
    +        error_pair(RobotImpairedError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_RECORDING:
    +        error_pair(IsRecordingError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_NO_PATH:
    +        error_pair(NoPathError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_NO_ANCHORING:
    +        error_pair(NoAnchoringError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_FEATURE_DESERT:
    +        error_pair(FeatureDesertError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_LOST:
    +        error_pair(RobotLostError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_NOT_LOCALIZED_TO_MAP:
    +        error_pair(RobotNotLocalizedToRouteError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_COULD_NOT_UPDATE_ROUTE:
    +        error_pair(RouteNotUpdatingError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_STUCK:
    +        error_pair(RobotStuckError),
    +    graph_nav_pb2.NavigateToAnchorResponse.STATUS_INVALID_POSE:
    +        error_pair(InvalidPoseError)
    +})
    +
    +
    +@handle_common_header_errors
    +@handle_lease_use_result_errors
    +@handle_unset_status_error(unset='STATUS_UNKNOWN')
    +def _navigate_to_anchor_error(response):
    +    """Return a custom exception based on navigate to anchor response, None if no error."""
    +    return error_factory(response, response.status,
    +                         status_to_string=graph_nav_pb2.NavigateToAnchorResponse.Status.Name,
    +                         status_to_error=_NAVIGATE_TO_ANCHOR_STATUS_TO_ERROR)
    +
    +
     @handle_common_header_errors
     @handle_unset_status_error(unset='STATUS_UNKNOWN')
     def _navigate_feedback_error(response):
    diff --git a/python/bosdyn-client/src/bosdyn/client/image.py b/python/bosdyn-client/src/bosdyn/client/image.py
    index 38e7d1e9a..037dcf3eb 100644
    --- a/python/bosdyn-client/src/bosdyn/client/image.py
    +++ b/python/bosdyn-client/src/bosdyn/client/image.py
    @@ -9,7 +9,8 @@
     import numpy as np
     import collections
     from bosdyn.client.common import BaseClient
    -from bosdyn.client.common import (error_factory, error_pair, common_header_errors, handle_common_header_errors)
    +from bosdyn.client.common import (error_factory, error_pair, common_header_errors,
    +                                  handle_common_header_errors)
     from bosdyn.client.exceptions import ResponseError, UnsetStatusError
     
     from bosdyn.api import image_pb2
    @@ -31,17 +32,24 @@ class SourceDataError(ImageResponseError):
     class ImageDataError(ImageResponseError):
         """System cannot generate image data for the ImageCapture at this time."""
     
    +
     class UnsupportedImageFormatRequestedError(ImageResponseError):
         """The image service cannot return data in the requested format."""
     
    +
     _STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
     _STATUS_TO_ERROR.update({
         image_pb2.ImageResponse.STATUS_OK: (None, None),
    -    image_pb2.ImageResponse.STATUS_UNKNOWN_CAMERA: error_pair(UnknownImageSourceError),
    -    image_pb2.ImageResponse.STATUS_SOURCE_DATA_ERROR: error_pair(SourceDataError),
    -    image_pb2.ImageResponse.STATUS_IMAGE_DATA_ERROR: error_pair(ImageDataError),
    -    image_pb2.ImageResponse.STATUS_UNSUPPORTED_IMAGE_FORMAT_REQUESTED: error_pair(UnsupportedImageFormatRequestedError),
    -    image_pb2.ImageResponse.STATUS_UNKNOWN: error_pair(UnsetStatusError),
    +    image_pb2.ImageResponse.STATUS_UNKNOWN_CAMERA:
    +        error_pair(UnknownImageSourceError),
    +    image_pb2.ImageResponse.STATUS_SOURCE_DATA_ERROR:
    +        error_pair(SourceDataError),
    +    image_pb2.ImageResponse.STATUS_IMAGE_DATA_ERROR:
    +        error_pair(ImageDataError),
    +    image_pb2.ImageResponse.STATUS_UNSUPPORTED_IMAGE_FORMAT_REQUESTED:
    +        error_pair(UnsupportedImageFormatRequestedError),
    +    image_pb2.ImageResponse.STATUS_UNKNOWN:
    +        error_pair(UnsetStatusError),
     })
     
     
    @@ -142,8 +150,7 @@ def _get_list_image_source_request():
             return image_pb2.ListImageSourcesRequest()
     
     
    -def build_image_request(image_source_name, quality_percent=75,
    -                        image_format=None):
    +def build_image_request(image_source_name, quality_percent=75, image_format=None):
         """Helper function which builds an ImageRequest from an image source name.
     
         By default the robot will choose an appropriate format when no image format
    @@ -180,10 +187,13 @@ def write_pgm_or_ppm(image_response, filename="", filepath="."):
             filepath(string): The directory to save the image.
         """
         # Determine the data type to decode the image.
    -    if image_response.shot.image.pixel_format == image_pb2.Image.PIXEL_FORMAT_DEPTH_U16:
    +    if image_response.shot.image.pixel_format in (image_pb2.Image.PIXEL_FORMAT_DEPTH_U16,
    +                                                  image_pb2.Image.PIXEL_FORMAT_GREYSCALE_U16):
             dtype = np.uint16
    +        max_val = 2 ** 16 - 1
         else:
             dtype = np.uint8
    +        max_val = 2 ** 8 - 1
     
         num_channels = 1
         pgm_header_number = 'P5'
    @@ -202,7 +212,7 @@ def write_pgm_or_ppm(image_response, filename="", filepath="."):
             num_channels = 1
         else:
             print("Unsupported pixel format for PGM/PPM: %s." %
    -            image_pb2.Image.PixelFormat.Name(image_response.shot.image.pixel_format))
    +              image_pb2.Image.PixelFormat.Name(image_response.shot.image.pixel_format))
             return
     
         img = np.frombuffer(image_response.shot.image.data, dtype=dtype)
    @@ -211,7 +221,9 @@ def write_pgm_or_ppm(image_response, filename="", filepath="."):
         try:
             img = img.reshape((height, width, num_channels))
         except ValueError as err:
    -        print("Cannot convert raw image into expected shape (rows %d, cols %d, color channels %d)." % (height, width, num_channels))
    +        print(
    +            "Cannot convert raw image into expected shape (rows %d, cols %d, color channels %d)." %
    +            (height, width, num_channels))
             print(err)
             return
         if not filename:
    @@ -224,12 +236,13 @@ def write_pgm_or_ppm(image_response, filename="", filepath="."):
             print("Cannot open file %s. Exception thrown: %s" % (filename, err))
             return
     
    -    max_val = np.amax(img)
    -    pgm_header = pgm_header_number + ' ' + str(width) + ' ' + str(height) + ' ' + str(max_val) + '\n'
    +    pgm_header = pgm_header_number + ' ' + str(width) + ' ' + str(height) + ' ' + str(
    +        max_val) + '\n'
         fd_out.write(pgm_header)
         img.tofile(fd_out)
    -    print('Saved matrix with pixel values from camera "%s" to file "%s".' % (
    -        image_response.source.name, filename))
    +    print('Saved matrix with pixel values from camera "%s" to file "%s".' %
    +          (image_response.source.name, filename))
    +
     
     def write_image_data(image_response, filename="", filepath="."):
         """Write image data from image_response to a file.
    @@ -252,6 +265,7 @@ def write_image_data(image_response, filename="", filepath="."):
             print('Failed to save "{}".'.format(image_response.source.name))
             print(err)
     
    +
     def save_images_as_files(image_responses, filename="", filepath="."):
         """Write image responses to files.
     
    @@ -275,4 +289,29 @@ def save_images_as_files(image_responses, filename="", filepath="."):
                 write_pgm_or_ppm(image, save_file_name, filepath)
             else:
                 # Save jpeg format as a jpeg image.
    -            write_image_data(image, save_file_name, filepath)
    \ No newline at end of file
    +            write_image_data(image, save_file_name, filepath)
    +
    +def pixel_to_camera_space(image_proto, pixel_x, pixel_y, depth=1.0):
    +    """Using the camera intrinsics, determine the (x,y,z) point in the camera frame for
    +    the (u,v) pixel coordinates.
    +
    +    Args:
    +        image_proto (image_pb2.Image): The image in which the pixel coordinates are from
    +        pixel_x (int): x-coordinate.
    +        pixel_y (int): y-coordinate.
    +        depth (double): The depth from the camera to the point of interest.
    +
    +    Returns:
    +        An (x,y,z) tuple representing the pixel point of interest now described as a point
    +        in the camera frame.
    +    """
    +    focal_x = image_proto.source.pinhole.intrinsics.focal_length.x
    +    principal_x = image_proto.source.pinhole.intrinsics.principal_point.x
    +
    +    focal_y = image_proto.source.pinhole.intrinsics.focal_length.y
    +    principal_y = image_proto.source.pinhole.intrinsics.principal_point.y
    +
    +    x_rt_camera = depth * (pixel_x - principal_x) / focal_x
    +    y_rt_camera = depth * (pixel_y - principal_y) / focal_y
    +    return (x_rt_camera, y_rt_camera, depth)
    +
    diff --git a/python/bosdyn-client/src/bosdyn/client/image_service_helpers.py b/python/bosdyn-client/src/bosdyn/client/image_service_helpers.py
    index 46661d207..c65bed5e0 100644
    --- a/python/bosdyn-client/src/bosdyn/client/image_service_helpers.py
    +++ b/python/bosdyn-client/src/bosdyn/client/image_service_helpers.py
    @@ -13,7 +13,8 @@
     import bosdyn.util
     from bosdyn.util import seconds_to_duration, sec_to_nsec
     from bosdyn.client.fault import FaultClient, ServiceFaultDoesNotExistError
    -from bosdyn.client.util import populate_response_header, setup_logging
    +from bosdyn.client.server_util import populate_response_header
    +from bosdyn.client.util import setup_logging
     
     from bosdyn.api import service_fault_pb2
     from bosdyn.api import header_pb2
    @@ -553,4 +554,3 @@ def GetImage(self, request, context):
         def __del__(self):
             for source in self.image_sources_mapped.values():
                 source.stop_capturing()
    -        super().__del__()
    diff --git a/python/bosdyn-client/src/bosdyn/client/ir_enable_disable.py b/python/bosdyn-client/src/bosdyn/client/ir_enable_disable.py
    new file mode 100644
    index 000000000..9ef851935
    --- /dev/null
    +++ b/python/bosdyn-client/src/bosdyn/client/ir_enable_disable.py
    @@ -0,0 +1,48 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""A client for the ir-enable-disable service."""
    +from bosdyn.client.common import common_header_errors
    +from bosdyn.client.common import BaseClient
    +from bosdyn.api import ir_enable_disable_pb2
    +from bosdyn.api import ir_enable_disable_service_pb2_grpc
    +
    +
    +class IREnableDisableServiceClient(BaseClient):
    +    """Client to enable and/or disable the robot's IR light emitters in the body and hand sensors."""
    +
    +    # Name of the service in the robot's directory listing.
    +    default_service_name = 'ir-enable-disable-service'
    +    # gRPC service proto definition implemented by this service
    +    service_type = 'bosdyn.api.IREnableDisableService'
    +
    +    def __init__(self):
    +        super(IREnableDisableServiceClient,
    +              self).__init__(ir_enable_disable_service_pb2_grpc.IREnableDisableServiceStub)
    +
    +    def set_ir_enabled(self, enable, **kwargs):
    +        """ Enable and/or disable the robot's IR light emitters.
    +
    +        Args:
    +            enable (bool): Whether or not to enable the emitters.
    +        """
    +        if enable:
    +            request = ir_enable_disable_pb2.IREnableDisableRequest.REQUEST_ON
    +        else:
    +            request = ir_enable_disable_pb2.IREnableDisableRequest.REQUEST_OFF
    +        return self.call(self._stub.IREnableDisable,
    +                         ir_enable_disable_pb2.IREnableDisableRequest(request=request),
    +                         error_from_response=common_header_errors, **kwargs)
    +
    +    def set_ir_enabled_async(self, enable, **kwargs):
    +        """Async version of set_ir_enabled()"""
    +        if enable:
    +            request = ir_enable_disable_pb2.IREnableDisableRequest.REQUEST_ON
    +        else:
    +            request = ir_enable_disable_pb2.IREnableDisableRequest.REQUEST_OFF
    +        return self.call_async(self._stub.IREnableDisable,
    +                               ir_enable_disable_pb2.IREnableDisableRequest(request=request),
    +                               error_from_response=common_header_errors, **kwargs)
    \ No newline at end of file
    diff --git a/python/bosdyn-client/src/bosdyn/client/lease.py b/python/bosdyn-client/src/bosdyn/client/lease.py
    index dfa9b369c..6a525e803 100644
    --- a/python/bosdyn-client/src/bosdyn/client/lease.py
    +++ b/python/bosdyn-client/src/bosdyn/client/lease.py
    @@ -9,18 +9,16 @@
     import collections
     import enum
     import logging
    +from bosdyn.api import lease_pb2
    +import six
     import threading
     import time
     
    +from bosdyn.api.lease_pb2 import AcquireLeaseRequest, AcquireLeaseResponse
     from bosdyn.api.lease_pb2 import Lease as LeaseProto
    -from bosdyn.api.lease_pb2 import AcquireLeaseRequest
    -from bosdyn.api.lease_pb2 import AcquireLeaseResponse
    -from bosdyn.api.lease_pb2 import ListLeasesRequest
    -from bosdyn.api.lease_pb2 import RetainLeaseRequest
    -from bosdyn.api.lease_pb2 import ReturnLeaseRequest
    -from bosdyn.api.lease_pb2 import ReturnLeaseResponse
    -from bosdyn.api.lease_pb2 import TakeLeaseRequest
    -from bosdyn.api.lease_pb2 import TakeLeaseResponse
    +from bosdyn.api.lease_pb2 import (LeaseUseResult, ListLeasesRequest, RetainLeaseRequest,
    +                                  ReturnLeaseRequest, ReturnLeaseResponse, TakeLeaseRequest,
    +                                  TakeLeaseResponse)
     from bosdyn.api.lease_service_pb2_grpc import LeaseServiceStub
     
     from . import common
    @@ -73,6 +71,7 @@ class Error(Exception):
     
     class NoSuchLease(Error):
         """The requested lease does not exist."""
    +
         def __init__(self, resource):
             self.resource = resource
     
    @@ -82,6 +81,7 @@ def __str__(self):
     
     class LeaseNotOwnedByWallet(Error):
         """The lease is not owned by the wallet."""
    +
         def __init__(self, resource, lease_state):
             self.resource = resource
             self.lease_state = lease_state
    @@ -97,30 +97,30 @@ def __str__(self):
     _ACQUIRE_LEASE_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
     _ACQUIRE_LEASE_STATUS_TO_ERROR.update({
         AcquireLeaseResponse.STATUS_OK: (None, None),
    -    AcquireLeaseResponse.STATUS_RESOURCE_ALREADY_CLAIMED: (ResourceAlreadyClaimedError,
    -                                                           ResourceAlreadyClaimedError.__doc__),
    -    AcquireLeaseResponse.STATUS_INVALID_RESOURCE: (InvalidResourceError,
    -                                                   InvalidResourceError.__doc__),
    -    AcquireLeaseResponse.STATUS_NOT_AUTHORITATIVE_SERVICE: (NotAuthoritativeServiceError,
    -                                                            NotAuthoritativeServiceError.__doc__),
    +    AcquireLeaseResponse.STATUS_RESOURCE_ALREADY_CLAIMED:
    +        (ResourceAlreadyClaimedError, ResourceAlreadyClaimedError.__doc__),
    +    AcquireLeaseResponse.STATUS_INVALID_RESOURCE:
    +        (InvalidResourceError, InvalidResourceError.__doc__),
    +    AcquireLeaseResponse.STATUS_NOT_AUTHORITATIVE_SERVICE:
    +        (NotAuthoritativeServiceError, NotAuthoritativeServiceError.__doc__),
     })
     
     _TAKE_LEASE_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
     _TAKE_LEASE_STATUS_TO_ERROR.update({
         TakeLeaseResponse.STATUS_OK: (None, None),
         TakeLeaseResponse.STATUS_INVALID_RESOURCE: (InvalidResourceError, InvalidResourceError.__doc__),
    -    TakeLeaseResponse.STATUS_NOT_AUTHORITATIVE_SERVICE: (NotAuthoritativeServiceError,
    -                                                         NotAuthoritativeServiceError.__doc__),
    +    TakeLeaseResponse.STATUS_NOT_AUTHORITATIVE_SERVICE:
    +        (NotAuthoritativeServiceError, NotAuthoritativeServiceError.__doc__),
     })
     
     _RETURN_LEASE_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
     _RETURN_LEASE_STATUS_TO_ERROR.update({
         ReturnLeaseResponse.STATUS_OK: (None, None),
    -    ReturnLeaseResponse.STATUS_INVALID_RESOURCE: (InvalidResourceError,
    -                                                  InvalidResourceError.__doc__),
    +    ReturnLeaseResponse.STATUS_INVALID_RESOURCE:
    +        (InvalidResourceError, InvalidResourceError.__doc__),
         ReturnLeaseResponse.STATUS_NOT_ACTIVE_LEASE: (NotActiveLeaseError, NotActiveLeaseError.__doc__),
    -    ReturnLeaseResponse.STATUS_NOT_AUTHORITATIVE_SERVICE: (NotAuthoritativeServiceError,
    -                                                           NotAuthoritativeServiceError.__doc__),
    +    ReturnLeaseResponse.STATUS_NOT_AUTHORITATIVE_SERVICE:
    +        (NotAuthoritativeServiceError, NotAuthoritativeServiceError.__doc__),
     })
     
     
    @@ -135,13 +135,13 @@ class Lease(object):
             lease_proto: bosdyn.api.Lease protobuf object.
         """
     
    -    def __init__(self, lease_proto):
    +    def __init__(self, lease_proto, ignore_is_valid_check=False):
             """Initializes a Lease object.
     
             Raises:
                 ValueError if lease_proto is not present or valid.
             """
    -        if not self.is_valid_proto(lease_proto):
    +        if not ignore_is_valid_check and not self.is_valid_proto(lease_proto):
                 raise ValueError('invalid lease_proto: {}'.format(lease_proto))
             self.lease_proto = lease_proto
     
    @@ -155,7 +155,7 @@ class CompareResult(enum.Enum):
             DIFFERENT_RESOURCES = 6
             DIFFERENT_EPOCHS = 7
     
    -    def compare(self, other_lease):
    +    def compare(self, other_lease, ignore_resources=False):
             """Compare two different lease objects.
     
             Args:
    @@ -176,10 +176,10 @@ def compare(self, other_lease):
                 * CompareResult.DIFFERENT_EPOCHS if this lease is for a different
                   epoch than other_lease. There is no way to compare recency/time of
                   Leases for two different epochs.
    -            * CompareResult.INVALID if either this or other_lease is invalid.
             """
             # Sequences are only valid for leases with the same resource and epoch.
    -        if not (self.lease_proto.resource == other_lease.lease_proto.resource):
    +        if not (self.lease_proto.resource
    +                == other_lease.lease_proto.resource) and not ignore_resources:
                 return self.CompareResult.DIFFERENT_RESOURCES
             if not (self.lease_proto.epoch == other_lease.lease_proto.epoch):
                 return self.CompareResult.DIFFERENT_EPOCHS
    @@ -236,6 +236,9 @@ def create_sublease(self, client_name=None):
                 sub_lease_proto.client_names.append(client_name)
             return Lease(sub_lease_proto)
     
    +    def is_valid_lease(self):
    +        return Lease.is_valid_proto(self.lease_proto)
    +
         @staticmethod
         def is_valid_proto(lease_proto):
             """Checks whether this lease is valid.
    @@ -245,6 +248,43 @@ def is_valid_proto(lease_proto):
             """
             return lease_proto and lease_proto.resource and lease_proto.sequence
     
    +    @staticmethod
    +    def compare_result_to_lease_use_result_status(compare_result, allow_super_leases):
    +        """Determines the comparable LeaseUseResult.Status enum value based on the CompareResult enum.
    +
    +        Args:
    +            allow_super_leases(boolean): If true, a super lease will still be considered as "ok"/
    +                                     newer when compared to the active lease.
    +
    +        Raises:
    +            bosdyn.client.lease.Error: Raised if there is an unknown compare result enum value.
    +
    +        Returns:
    +            The corresponding LeaseUseResult.Status enum value.
    +        """
    +        if compare_result == Lease.CompareResult.DIFFERENT_EPOCHS:
    +            return LeaseUseResult.STATUS_WRONG_EPOCH
    +        elif compare_result == Lease.CompareResult.DIFFERENT_RESOURCES:
    +            # if the incoming lease's resource doesn't match the active lease resource,
    +            # then mark it as unmanaged.
    +            return LeaseUseResult.STATUS_UNMANAGED
    +        elif compare_result == Lease.CompareResult.SUPER_LEASE:
    +            # In some cases we may want to allow a super lease, so check an optional boolean
    +            # to see if that is the case.
    +            if allow_super_leases:
    +                return LeaseUseResult.STATUS_OK
    +            # In the normal case, a super lease is considered older.
    +            return LeaseUseResult.STATUS_OLDER
    +        elif compare_result == Lease.CompareResult.OLDER:
    +            return LeaseUseResult.STATUS_OLDER
    +        elif (compare_result == Lease.CompareResult.SAME or
    +              compare_result == Lease.CompareResult.SUB_LEASE or
    +              compare_result == Lease.CompareResult.NEWER):
    +            return LeaseUseResult.STATUS_OK
    +        else:
    +            # Shouldn't hit here. The above set of checks should be exhaustive.
    +            raise Error("The comparison result of the leases is unknown/unaccounted for.")
    +
     
     class LeaseState(object):
     
    @@ -262,7 +302,8 @@ class Status(enum.Enum):
         STATUS_OTHER_OWNER = Status.OTHER_OWNER
         STATUS_NOT_MANAGED = Status.NOT_MANAGED
     
    -    def __init__(self, lease_status, lease_owner=None, lease=None, lease_current=None, client_name=None):
    +    def __init__(self, lease_status, lease_owner=None, lease=None, lease_current=None,
    +                 client_name=None):
             """
             Args:
                 lease_status(LeaseState.Status): The ownership status of the lease.
    @@ -342,8 +383,12 @@ def add(self, lease):
                 lease: Lease to add in the wallet.
             """
             with self._lock:
    -            self._lease_state_map[lease.lease_proto.resource] = LeaseState(
    -                LeaseState.Status.SELF_OWNER, lease=lease, client_name=self.client_name)
    +            self._add_lease_locked(lease)
    +
    +    def _add_lease_locked(self, lease, current=False):
    +        resource = lease.lease_proto.resource
    +        self._lease_state_map[resource] = LeaseState(LeaseState.Status.SELF_OWNER, lease=lease,
    +                                                     client_name=self.client_name)
     
         def remove(self, lease):
             """Remove lease from the wallet.
    @@ -519,7 +564,7 @@ def return_lease(self, lease, **kwargs):
             """Return an acquired lease.
     
             Args:
    -            lease: Lease to return.
    +            lease (Lease object): Lease to return. This should be a Lease class object, and not the proto.
     
             Raises:
                 InvalidResourceError: Resource is not known to the LeaseService.
    @@ -558,6 +603,7 @@ def retain_lease_async(self, lease, **kwargs):
             return self.call_async(self._stub.RetainLease, req, None, common.common_lease_errors,
                                    **kwargs)
     
    +
         def list_leases(self, include_full_lease_info=False, **kwargs):
             """Get a list of the leases.
     
    @@ -583,6 +629,32 @@ def list_leases_async(self, include_full_lease_info=False, **kwargs):
             return self.call_async(self._stub.ListLeases, req, self._list_leases_success,
                                    common.common_header_errors, **kwargs)
     
    +    def list_leases_full(self, include_full_lease_info=False, **kwargs):
    +        """Get a list of the leases.
    +
    +        Args:
    +            include_full_lease_info: Whether the returned list of LeaseResources should include
    +                                     all of the available information about the last lease used.
    +                                     Defaults to False.
    +
    +        Returns:
    +            The complete ListLeasesResponse message.
    +
    +        Raises:
    +            InternalServerError: Service experienced an unexpected error state.
    +            LeaseUseError: Request was rejected due to using an invalid lease.
    +        """
    +        req = self._make_list_leases_request(include_full_lease_info)
    +        return self.call(self._stub.ListLeases, req, None,
    +                         common.common_header_errors, **kwargs)
    +
    +    def list_leases_full_async(self, include_full_lease_info=False, **kwargs):
    +        """Async version of the list_leases() function."""
    +        req = self._make_list_leases_request(include_full_lease_info)
    +        return self.call_async(self._stub.ListLeases, req, None,
    +                               common.common_header_errors, **kwargs)
    +
    +
         @staticmethod
         def _make_acquire_request(resource):
             """Return AcquireLeaseRequest message with the given resource."""
    @@ -775,11 +847,20 @@ class LeaseKeepAlive(object):
                     False if the UI thread is wedged, which prevents the
                     application from continuing to keep the Lease alive when it is
                     no longer in a good state.
    +        on_failure_callback: If specified, this should be a callable function object
    +                which takes the error/exception as an input. The  function does not
    +                need to return anything. This function can be used to action on any
    +                failures during the keepalive from the RetainLease RPC.
    +        warnings: Boolean used to determine if the _periodic_check_in function will print lease check-in errors.
         """
     
         def __init__(self, lease_client, lease_wallet=None, resource=_RESOURCE_BODY,
    -                 rpc_interval_seconds=2, keep_running_cb=None):
    +                 rpc_interval_seconds=2, keep_running_cb=None, host_name="",
    +                 on_failure_callback=None, warnings=True, return_at_exit=False):
             """Create a new LeaseKeepAlive object."""
    +        self.host_name = host_name
    +        self.print_warnings = warnings
    +        self._return_at_exit = return_at_exit
             if not lease_client:
                 raise ValueError("lease_client must be set")
             self._lease_client = lease_client
    @@ -804,6 +885,9 @@ def __init__(self, lease_client, lease_wallet=None, resource=_RESOURCE_BODY,
     
             self._end_check_in_signal = threading.Event()
     
    +        # If the on_failure_callback is not provided, then set the default as a no-op function.
    +        self._retain_lease_failed_cb = on_failure_callback or (lambda err: None)
    +
             # Configure the thread to do check-ins, and begin checking in.
             self._thread = threading.Thread(target=self._periodic_check_in)
             self._thread.daemon = True
    @@ -846,6 +930,8 @@ def __enter__(self):
     
         def __exit__(self, exc_type, exc_val, exc_tb):
             self.shutdown()
    +        if self._return_at_exit:
    +            self._lease_client.return_lease(self.lease_wallet.get_lease(self._resource))
     
         def _ok(self):
             self.logger.debug('Check-in successful')
    @@ -874,8 +960,11 @@ def _periodic_check_in(self):
                 # We really do want to catch anything.
                 #pylint: disable=broad-except
                 except Exception as exc:
    -                self.logger.warning('Generic exception during check-in:\n%s\n'
    -                                    '    (resuming check-in)', exc)
    +                if self.print_warnings:
    +                    self.logger.warning(
    +                        'Generic exception for %s during check-in:\n%s\n'
    +                        '    (resuming check-in)', self.host_name, exc)
    +                self._retain_lease_failed_cb(exc)
                 else:
                     # No errors!
                     self._ok()
    @@ -890,3 +979,70 @@ def _periodic_check_in(self):
                 if self._end_check_in_signal.wait(self._rpc_interval_seconds - exec_seconds):
                     break
             self.logger.info('Lease check-in stopped')
    +
    +
    +def test_active_lease(incoming_lease_proto, active_lease, sublease_name=None,
    +                      allow_super_leases=False):
    +    """Check if an incoming lease is newer than the current lease.
    +
    +    Args:
    +        incoming_lease_proto(lease_pb2.Lease): The incoming lease proto.
    +        active_lease(Lease): A lease object representing the most recent/newest known lease
    +                             that the incoming lease should be compared against.
    +        sublease_name(string): If not NoneType, a sublease of the incoming lease will be
    +                             created (with sublease_name as the client name) and used to compare to
    +                             the active lease.
    +        allow_super_leases(boolean): If true, a super lease will still be considered as "ok"/
    +                                     newer when compared to the active lease.
    +
    +    Returns:
    +        A tuple containing the lease use result from comparing the incoming and active leases, and
    +        then a Lease object made from the incoming lease proto. The lease object will be None if
    +        the incoming lease proto was invalid. The lease object will be a sublease of the incoming
    +        lease proto if sublease_name is not NoneType.
    +    """
    +    lease_use_result = LeaseUseResult()
    +    lease_use_result.attempted_lease.CopyFrom(incoming_lease_proto)
    +
    +    # Ensure the incoming lease is valid. If it is valid, create a sublease for the
    +    # incoming lease and test with that.
    +    try:
    +        incoming_lease = Lease(incoming_lease_proto)
    +        if sublease_name is not None:
    +            incoming_lease = incoming_lease.create_sublease(client_name=sublease_name)
    +    except ValueError:
    +        # Invalid lease proto will throw this error.
    +        lease_use_result.status = LeaseUseResult.STATUS_INVALID_LEASE
    +        return lease_use_result, None
    +
    +    if active_lease is None:
    +        # No active lease means that the incoming lease is good!
    +        # Set the incoming lease proto as the latest known lease. There will be no previous
    +        # lease since this is the first one for the resource.
    +        lease_use_result.latest_known_lease.CopyFrom(incoming_lease.lease_proto)
    +        lease_use_result.status = LeaseUseResult.STATUS_OK
    +        return lease_use_result, incoming_lease
    +
    +    # Ensure the active lease is also valid.
    +    if not active_lease.is_valid_lease():
    +        # Raise an exception since the incoming lease is invalid.
    +        raise Error("The active lease object is invalid.")
    +
    +    # Set the previous lease as the active lease.
    +    lease_use_result.previous_lease.CopyFrom(active_lease.lease_proto)
    +
    +    # Set the latest known lease as the active lease. This will be overwritten if the incoming
    +    # lease is found to be newer.
    +    lease_use_result.latest_known_lease.CopyFrom(active_lease.lease_proto)
    +
    +    # Compare the incoming lease's sublease to the latest known/most recent lease (active lease).
    +    compare_result = incoming_lease.compare(active_lease)
    +    lease_use_result.status = Lease.compare_result_to_lease_use_result_status(
    +        compare_result, allow_super_leases)
    +    if lease_use_result.status == LeaseUseResult.STATUS_OK and compare_result != Lease.CompareResult.SUPER_LEASE:
    +        # Only update the latest known lease if the incoming lease was found as status ok (newer/the same
    +        # as the existing lease). Also prevents a "super-lease" from getting set as the
    +        # latest known lease (when the allow_super_lease boolean is True) when there have been newer
    +        # subleases seen.
    +        lease_use_result.latest_known_lease.CopyFrom(incoming_lease.lease_proto)
    +    return lease_use_result, incoming_lease
    diff --git a/python/bosdyn-client/src/bosdyn/client/license.py b/python/bosdyn-client/src/bosdyn/client/license.py
    index 41c4d0005..9770f03ad 100644
    --- a/python/bosdyn-client/src/bosdyn/client/license.py
    +++ b/python/bosdyn-client/src/bosdyn/client/license.py
    @@ -9,9 +9,11 @@
     from bosdyn.api import license_pb2, license_service_pb2_grpc
     from bosdyn.client.common import BaseClient, common_header_errors
     
    +
     def _get_entry_value(response):
         return response.license
     
    +
     class LicenseClient(BaseClient):
         """Client to acquire robot license."""
         # Typical name of the service in the robot's directory listing.
    diff --git a/python/bosdyn-client/src/bosdyn/client/local_grid.py b/python/bosdyn-client/src/bosdyn/client/local_grid.py
    index 41c7c318c..7f738383a 100644
    --- a/python/bosdyn-client/src/bosdyn/client/local_grid.py
    +++ b/python/bosdyn-client/src/bosdyn/client/local_grid.py
    @@ -36,7 +36,8 @@ def get_local_grid_types(self, **kwargs):
     
         def get_local_grid_types_async(self, **kwargs):
             """Async version of get_local_grid_types()."""
    -        return self.call_async(self._stub.GetLocalGridTypes, local_grid_pb2.GetLocalGridTypesRequest(),
    +        return self.call_async(self._stub.GetLocalGridTypes,
    +                               local_grid_pb2.GetLocalGridTypesRequest(),
                                    value_from_response=lambda res: res.local_grid_type,
                                    error_from_response=common_header_errors, **kwargs)
     
    @@ -60,7 +61,6 @@ def get_local_grids(self, local_grid_type_names, **kwargs):
                              value_from_response=lambda res: res.local_grid_responses,
                              error_from_response=common_header_errors, **kwargs)
     
    -
         def get_local_grids_async(self, local_grid_type_names, **kwargs):
             """Async version of get_local_grids()."""
             request = local_grid_pb2.GetLocalGridsRequest()
    diff --git a/python/bosdyn-client/src/bosdyn/client/log_annotation.py b/python/bosdyn-client/src/bosdyn/client/log_annotation.py
    index 1dfe5d9cf..45a37597f 100644
    --- a/python/bosdyn-client/src/bosdyn/client/log_annotation.py
    +++ b/python/bosdyn-client/src/bosdyn/client/log_annotation.py
    @@ -30,13 +30,13 @@
     import bosdyn.api.log_annotation_pb2 as log_annotation_protos
     import bosdyn.api.log_annotation_service_pb2_grpc as log_annotation_service
     
    +
     class InvalidArgument(Error):
         """A given argument could not be used."""
     
    -@deprecated(
    -    reason='The log-annotation client and service have been replaced by data_buffer.',
    -    version='2.1.0',
    -    action="always")
    +
    +@deprecated(reason='The log-annotation client and service have been replaced by data_buffer.',
    +            version='2.1.0', action="always")
     class LogAnnotationClient(BaseClient):
         """A client for adding annotations to robot logs."""
     
    @@ -156,19 +156,17 @@ def _now_in_robot_basis(self, msg_type=None, proto=None):
                     converter = self._timesync_endpoint.get_robot_time_converter()
                 except time_sync.NotEstablishedError:
                     # No timesync. That's OK -- the receiving host will provide the timestamp.
    -                self.logger.debug('Could not timestamp message of type %s',
    -                                  (msg_type if msg_type is not None
    -                                   else (proto.DESCRIPTOR.full_name if proto is not None
    -                                         else 'Unknown')))
    +                self.logger.debug(
    +                    'Could not timestamp message of type %s',
    +                    (msg_type if msg_type is not None else
    +                     (proto.DESCRIPTOR.full_name if proto is not None else 'Unknown')))
                 else:
                     return converter.robot_timestamp_from_local_secs(time.time())
             return None
     
     
    -@deprecated(
    -    reason='The log-annotation client and service have been replaced by data_buffer.',
    -    version='2.1.0',
    -    action="always")
    +@deprecated(reason='The log-annotation client and service have been replaced by data_buffer.',
    +            version='2.1.0', action="always")
     class LogAnnotationHandler(logging.Handler):
         """A logging system Handler that will publish text to a bosdyn.api.LogAnnotationService.
     
    diff --git a/python/bosdyn-client/src/bosdyn/client/manipulation_api_client.py b/python/bosdyn-client/src/bosdyn/client/manipulation_api_client.py
    index a0961886c..be8734573 100644
    --- a/python/bosdyn-client/src/bosdyn/client/manipulation_api_client.py
    +++ b/python/bosdyn-client/src/bosdyn/client/manipulation_api_client.py
    @@ -6,12 +6,14 @@
     
     """For clients to the Manipulation API service."""
     
    -from bosdyn.client.common import BaseClient
    +from bosdyn.client.common import (BaseClient, handle_common_header_errors,
    +                                  handle_lease_use_result_errors)
     
     from bosdyn.api import manipulation_api_service_pb2_grpc
     
     from .lease import add_lease_wallet_processors
     
    +
     class ManipulationApiClient(BaseClient):
         """Client for the ManipulationAPI service."""
         default_service_name = 'manipulation'
    @@ -41,7 +43,9 @@ def manipulation_api_command(self, manipulation_api_request, **kwargs):
             Returns:
                 The full ManipulationApiResponse message, which includes a command id for feedback.
             """
    -        return self.call(self._stub.ManipulationApi, manipulation_api_request, **kwargs)
    +        return self.call(self._stub.ManipulationApi, manipulation_api_request,
    +                         error_from_response=_manipulation_api_command_error_from_response,
    +                         **kwargs)
     
         def manipulation_api_command_async(self, manipulation_api_request, **kwargs):
             """Async version of the manipulation_api_command()."""
    @@ -57,8 +61,44 @@ def manipulation_api_feedback_command(self, manipulation_api_feedback_request, *
             Returns:
                 The full ManipulationApiFeedbackResponse message.
             """
    -        return self.call(self._stub.ManipulationApiFeedback, manipulation_api_feedback_request, **kwargs)
    +        return self.call(self._stub.ManipulationApiFeedback, manipulation_api_feedback_request,
    +                         error_from_response=_manipulation_api_feedback_error_from_response,
    +                         **kwargs)
     
         def manipulation_api_feedback_command_async(self, manipulation_api_feedback_request, **kwargs):
             """Async version of the manipulation_api_feedback_command()."""
    -        return self.call_async(self._stub.ManipulationApiFeedback, manipulation_api_feedback_request, **kwargs)
    +        return self.call_async(self._stub.ManipulationApiFeedback,
    +                               manipulation_api_feedback_request, **kwargs)
    +
    +    def grasp_override_command(self, grasp_override_request, **kwargs):
    +        """Issue a grasp override command to the robot.
    +
    +        Args:
    +            grasp_override_request (manipulation_api_pb2.ApiGraspOverrideRequest): The command request
    +                for a grasp override.
    +
    +        Returns:
    +            An ApiGraspOverrideResponse message.
    +        """
    +        return self.call(self._stub.OverrideGrasp, grasp_override_request,
    +                         error_from_response=_grasp_override_command_error_from_response, **kwargs)
    +
    +    def grasp_override_command_async(self, grasp_override_request, **kwargs):
    +        """Async version of the grasp_override_command()."""
    +        return self.call_async(self._stub.OverrideGrasp, grasp_override_request, **kwargs)
    +
    +
    +@handle_common_header_errors
    +@handle_lease_use_result_errors
    +def _manipulation_api_command_error_from_response(response):
    +    return None
    +
    +
    +@handle_common_header_errors
    +def _manipulation_api_feedback_error_from_response(response):
    +    return None
    +
    +
    +@handle_common_header_errors
    +def _grasp_override_command_error_from_response(response):
    +    return None
    \ No newline at end of file
    diff --git a/python/bosdyn-client/src/bosdyn/client/map_processing.py b/python/bosdyn-client/src/bosdyn/client/map_processing.py
    new file mode 100644
    index 000000000..cb845b286
    --- /dev/null
    +++ b/python/bosdyn-client/src/bosdyn/client/map_processing.py
    @@ -0,0 +1,215 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""For clients of the graph_nav map processing service."""
    +from __future__ import print_function
    +import collections
    +from bosdyn.api.graph_nav import map_pb2
    +from bosdyn.api.graph_nav import map_processing_pb2
    +from bosdyn.api.graph_nav import map_processing_service_pb2
    +from bosdyn.api.graph_nav import map_processing_service_pb2_grpc as map_processing
    +from bosdyn.client.common import BaseClient
    +from bosdyn.client.common import (BaseClient, error_factory, handle_unset_status_error,
    +                                  handle_common_header_errors, handle_lease_use_result_errors,
    +                                  common_header_errors)
    +from enum import Enum
    +from bosdyn.client.exceptions import ResponseError
    +
    +
    +class MapProcessingServiceResponseError(ResponseError):
    +    """General class of errors for the GraphNav map processing service."""
    +
    +
    +class MissingSnapshotsError(MapProcessingServiceResponseError):
    +    """The uploaded map has missing waypoint snapshots."""
    +
    +
    +class OptimizationFailureError(MapProcessingServiceResponseError):
    +    """The anchoring optimization failed."""
    +
    +
    +class InvalidGraphError(MapProcessingServiceResponseError):
    +    """The graph is invalid topologically, for example containing missing waypoints referenced by edges."""
    +
    +
    +class InvalidParamsError(MapProcessingServiceResponseError):
    +    """The parameters passed to the optimizer do not make sense (e.g negative weights)."""
    +
    +
    +class MaxIterationsError(MapProcessingServiceResponseError):
    +    """The optimizer reached the maximum number of iterations before converging."""
    +
    +
    +class MaxTimeError(MapProcessingServiceResponseError):
    +    """The optimizer timed out before converging."""
    +
    +
    +class InvalidHintsError(MapProcessingServiceResponseError):
    +    """One or more of the hints passed in to the optimizer are invalid (do not correspond to real waypoints or objects)."""
    +
    +
    +class ConstraintViolationError(MapProcessingServiceResponseError):
    +    """One or more anchors were moved outside of the desired constraints."""
    +
    +class MapModifiedError(MapProcessingServiceResponseError):
    +    """The map was modified on the server by another client during processing. Please try again."""
    +
    +
    +@handle_common_header_errors
    +@handle_unset_status_error(unset='STATUS_UNKNOWN')
    +def _process_topology_common_errors(response):
    +    # Handle error statuses from the request.
    +    if (response.status == map_processing_pb2.ProcessTopologyResponse.STATUS_INVALID_GRAPH):
    +        return InvalidGraphError(response=response, error_message=InvalidGraphError.__doc__)
    +    elif (response.status ==
    +          map_processing_pb2.ProcessTopologyResponse.STATUS_MISSING_WAYPOINT_SNAPSHOTS):
    +        return MissingSnapshotsError(response=response, error_message=MissingSnapshotsError.__doc__)
    +    elif (response.status ==
    +          map_processing_pb2.ProcessTopologyResponse.STATUS_MAP_MODIFIED_DURING_PROCESSING):
    +        return MapModifiedError(response=response, error_message=MapModifiedError.__doc__)
    +    return None
    +
    +
    +def _process_topology_streamed_errors(responses):
    +    """Return a custom exception based on process topology streaming response, None if no error."""
    +    # Iterate through the response since the request responds with a stream.
    +    for resp in responses:
    +        exception = _process_topology_common_errors(resp)
    +        if exception:
    +            return exception
    +
    +    # All responses (in the iterator) had status_ok
    +    return None
    +
    +
    +__ANCHORING_COMMON_ERRORS = {
    +    map_processing_pb2.ProcessAnchoringResponse.STATUS_MISSING_WAYPOINT_SNAPSHOTS:
    +        MissingSnapshotsError,
    +    map_processing_pb2.ProcessAnchoringResponse.STATUS_OPTIMIZATION_FAILURE:
    +        OptimizationFailureError,
    +    map_processing_pb2.ProcessAnchoringResponse.STATUS_INVALID_GRAPH:
    +        InvalidGraphError,
    +    map_processing_pb2.ProcessAnchoringResponse.STATUS_INVALID_PARAMS:
    +        InvalidParamsError,
    +    map_processing_pb2.ProcessAnchoringResponse.STATUS_CONSTRAINT_VIOLATION:
    +        ConstraintViolationError,
    +    map_processing_pb2.ProcessAnchoringResponse.STATUS_MAX_ITERATIONS:
    +        MaxIterationsError,
    +    map_processing_pb2.ProcessAnchoringResponse.STATUS_MAX_TIME:
    +        MaxTimeError,
    +    map_processing_pb2.ProcessAnchoringResponse.STATUS_INVALID_HINTS:
    +        InvalidHintsError,
    +    map_processing_pb2.ProcessAnchoringResponse.STATUS_MAP_MODIFIED_DURING_PROCESSING:
    +        MapModifiedError
    +}
    +
    +
    +@handle_common_header_errors
    +@handle_unset_status_error(unset='STATUS_UNKNOWN')
    +def _process_anchoring_common_errors(response):
    +    # Handle error statuses from the request.
    +    if response.status in __ANCHORING_COMMON_ERRORS:
    +        type_name = __ANCHORING_COMMON_ERRORS[response.status]
    +        return type_name(response=response, error_message=type_name.__doc__)
    +    return None
    +
    +
    +def _process_anchoring_streamed_errors(responses):
    +    """Return a custom exception based on process anchoring streaming response, None if no error."""
    +    # Iterate through the response since the request responds with a stream.
    +    for resp in responses:
    +        exception = _process_anchoring_common_errors(resp)
    +        if exception:
    +            return exception
    +
    +    # All responses (in the iterator) had status_ok
    +    return None
    +
    +
    +def _get_streamed_topology_response(response):
    +    """Reads a streamed response to recreate a merged topology response."""
    +    merged_response = map_processing_pb2.ProcessTopologyResponse()
    +
    +    for resp in response:
    +        merged_response.MergeFrom(resp)
    +    return merged_response
    +
    +
    +def _get_streamed_anchoring_response(response):
    +    """Reads a streamed response to recreate a merged anchoring response."""
    +    merged_response = map_processing_pb2.ProcessAnchoringResponse()
    +
    +    for resp in response:
    +        merged_response.MergeFrom(resp)
    +    return merged_response
    +
    +
    +class MapProcessingServiceClient(BaseClient):
    +    """Client for the GraphNav map processing service."""
    +    default_service_name = 'map-processing-service'
    +    service_type = 'bosdyn.api.graph_nav.MapProcessingService'
    +
    +    def __init__(self):
    +        super(MapProcessingServiceClient, self).__init__(map_processing.MapProcessingServiceStub)
    +
    +    @staticmethod
    +    def _build_process_topology_request(params, modify_map_on_server):
    +        return map_processing_pb2.ProcessTopologyRequest(params=params,
    +                                                         modify_map_on_server=modify_map_on_server)
    +
    +    @staticmethod
    +    def _build_process_anchoring_request(params, modify_anchoring_on_server,
    +                                         stream_intermediate_results, initial_hint):
    +        return map_processing_pb2.ProcessAnchoringRequest(
    +            params=params, initial_hint=initial_hint,
    +            modify_anchoring_on_server=modify_anchoring_on_server,
    +            stream_intermediate_results=stream_intermediate_results)
    +
    +    def process_topology(self, params, modify_map_on_server, **kwargs):
    +        """Process the topology of the map on the server, closing loops and producing a
    +         consistent topology.
    +
    +        Args:
    +            params: a ProcessTopologyRequest.Params object
    +            modify_map_on_server: if true, the map will be modified on the server. If false,
    +            the subgraph returned by this function should be uploaded back to the server if it
    +            is to be reused.
    +
    +        Returns:
    +            The ProcessTopologyResponse containing new edges to add to the map.
    +
    +        Raises:
    +            RpcError: Problem communicating with the robot
    +        """
    +        request = self._build_process_topology_request(params, modify_map_on_server)
    +        return self.call(self._stub.ProcessTopology, request,
    +                         value_from_response=_get_streamed_topology_response,
    +                         error_from_response=_process_topology_streamed_errors, **kwargs)
    +
    +    def process_anchoring(self, params, modify_anchoring_on_server, stream_intermediate_results,
    +                          initial_hint=None, **kwargs):
    +        """Process the anchoring of the map on the server, producing a metrically consistent anchoring.
    +
    +        Args:
    +            params: a ProcessAnchoringRequest.Params object
    +            modify_anchoring_on_server: if true, the map will be modified on the server. If false,
    +            the anchoring returned by this function should be uploaded back to the server if it
    +            is to be reused.
    +            stream_intermediate_results: if true, anchorings from earlier optimizer
    +            iterations may be included in the response. If false, only the last iteration will be returned.
    +            initial_hint: Initial guess at some number of waypoints and world objects and their anchorings.
    +            This field is an AnchoringHint object (see map_processing.proto)
    +        Returns:
    +            The ProcessAnchoringResponse containing a new optimized anchoring.
    +        Raises:
    +            RpcError: Problem communicating with the robot
    +        """
    +        request = self._build_process_anchoring_request(params, modify_anchoring_on_server,
    +                                                        stream_intermediate_results, initial_hint)
    +
    +        return self.call(self._stub.ProcessAnchoring, request,
    +                         value_from_response=_get_streamed_anchoring_response,
    +                         error_from_response=_process_anchoring_streamed_errors, **kwargs)
    diff --git a/python/bosdyn-client/src/bosdyn/client/math_helpers.py b/python/bosdyn-client/src/bosdyn/client/math_helpers.py
    index d95d9c3b7..41f9b2f8e 100644
    --- a/python/bosdyn-client/src/bosdyn/client/math_helpers.py
    +++ b/python/bosdyn-client/src/bosdyn/client/math_helpers.py
    @@ -18,6 +18,7 @@ def angle_diff(a1, a2):
             v += 2 * math.pi
         return v
     
    +
     def angle_diff_degrees(a1, a2):
         v = a1 - a2
         while v > 180:
    @@ -26,6 +27,7 @@ def angle_diff_degrees(a1, a2):
             v += 360
         return v
     
    +
     class SE2Pose(object):
         """Class representing an SE2Pose with position and angle."""
     
    @@ -64,8 +66,8 @@ def to_obj(self, proto):
     
         def to_proto(self):
             """Converts the math_helpers.SE2Pose into an output of the protobuf geometry_pb2.SE2Pose."""
    -        return geometry_pb2.SE2Pose(
    -            position=geometry_pb2.Vec2(x=self.x, y=self.y), angle=self.angle)
    +        return geometry_pb2.SE2Pose(position=geometry_pb2.Vec2(x=self.x, y=self.y),
    +                                    angle=self.angle)
     
         def inverse(self):
             """
    @@ -159,13 +161,14 @@ def get_closest_se3_transform(self, height_z=0.0):
             # the correct height is passed into the function as height_z.
             return SE3Pose(x=self.x, y=self.y, z=height_z, rot=Quat.from_yaw(self.angle))
     
    +
     class SE2Velocity(object):
         """Class representing an SE2Velocity with linear velocity and angular velocity."""
     
         def __init__(self, x, y, angular):
    -        self.linear_velocity_x = x
    -        self.linear_velocity_y = y
    -        self.angular_velocity = angular
    +        self.linear_velocity_x = float(x)
    +        self.linear_velocity_y = float(y)
    +        self.angular_velocity = float(angular)
     
         def __str__(self):
             return 'Linear velocity -- X: %0.3f Y: %0.3f Angular velocity -- %0.3f ' % (
    @@ -186,7 +189,8 @@ def to_proto(self):
         def to_vector(self):
             """Creates a 3x1 velocity vector as a numpy array."""
             # Create a 3x1 vector of [x_d, y_d, r_d]
    -        return numpy.array([self.linear_velocity_x, self.linear_velocity_y, self.angular_velocity]).reshape((3,1))
    +        return numpy.array([self.linear_velocity_x, self.linear_velocity_y,
    +                            self.angular_velocity]).reshape((3, 1))
     
         @staticmethod
         def from_vector(se2_vel_vector):
    @@ -194,12 +198,12 @@ def from_vector(se2_vel_vector):
             if type(se2_vel_vector) == list:
                 if len(se2_vel_vector) != 3:
                     # Must have 3 elements to be a complete SE2Velocity
    -                print(
    -                    "Velocity list must have 3 elements. The input has the wrong dimension of: "
    -                    + str(len(se2_vel_vector)))
    +                print("Velocity list must have 3 elements. The input has the wrong dimension of: " +
    +                      str(len(se2_vel_vector)))
                     return None
                 else:
    -                return SE2Velocity(x=se2_vel_vector[0], y=se2_vel_vector[1], angular=se2_vel_vector[2])
    +                return SE2Velocity(x=se2_vel_vector[0], y=se2_vel_vector[1],
    +                                   angular=se2_vel_vector[2])
             if type(se2_vel_vector) == numpy.ndarray:
                 if se2_vel_vector.shape[0] != 3:
                     # Must have 3 elements to be a complete SE2Velocity
    @@ -233,12 +237,12 @@ class SE3Velocity(object):
         """Class representing an SE3Velocity with linear velocity and angular velocity."""
     
         def __init__(self, lin_x, lin_y, lin_z, ang_x, ang_y, ang_z):
    -        self.linear_velocity_x = lin_x
    -        self.linear_velocity_y = lin_y
    -        self.linear_velocity_z = lin_z
    -        self.angular_velocity_x = ang_x
    -        self.angular_velocity_y = ang_y
    -        self.angular_velocity_z = ang_z
    +        self.linear_velocity_x = float(lin_x)
    +        self.linear_velocity_y = float(lin_y)
    +        self.linear_velocity_z = float(lin_z)
    +        self.angular_velocity_x = float(ang_x)
    +        self.angular_velocity_y = float(ang_y)
    +        self.angular_velocity_z = float(ang_z)
     
         def __str__(self):
             return 'Linear velocity -- X: %0.3f Y: %0.3f Z: %0.3f Angular velocity -- X: %0.3f Y: %0.3f Z: %0.3f' % (
    @@ -268,7 +272,7 @@ def to_vector(self):
             return numpy.array([
                 self.linear_velocity_x, self.linear_velocity_y, self.linear_velocity_z,
                 self.angular_velocity_x, self.angular_velocity_y, self.angular_velocity_z
    -        ]).reshape((6,1))
    +        ]).reshape((6, 1))
     
         @property
         def linear(self):
    @@ -296,9 +300,8 @@ def from_vector(se3_vel_vector):
             if type(se3_vel_vector) == list:
                 if len(se3_vel_vector) != 6:
                     # Must have 6 elements to be a complete SE3Velocity
    -                print(
    -                    "Velocity list must have 6 elements. The input has the wrong dimension of: "
    -                    + str(len(se3_vel_vector)))
    +                print("Velocity list must have 6 elements. The input has the wrong dimension of: " +
    +                      str(len(se3_vel_vector)))
                     return None
                 else:
                     return SE3Velocity(lin_x=se3_vel_vector[0], lin_y=se3_vel_vector[1],
    @@ -474,6 +477,7 @@ def from_matrix(mat):
             rot = Quat.from_matrix(mat[0:3, 0:3])
             return SE3Pose(x, y, z, rot)
     
    +
         @staticmethod
         def from_identity():
             """Create a math_helpers.SE3Pose representing the identity SE(3) pose."""
    @@ -497,7 +501,7 @@ def to_adjoint_matrix(self):
             a_R_b = self.rot.to_matrix()
             position_skew_mat = skew_matrix_3d(self.position)
             mat = numpy.matmul(position_skew_mat, a_R_b)
    -        a_adjoint_b = numpy.block([[a_R_b, mat], [numpy.zeros((3,3)), a_R_b]])
    +        a_adjoint_b = numpy.block([[a_R_b, mat], [numpy.zeros((3, 3)), a_R_b]])
             return a_adjoint_b
     
         def get_closest_se2_transform(self):
    @@ -508,6 +512,24 @@ def get_closest_se2_transform(self):
             se2_angle = (self.rot.closest_yaw_only_quaternion()).to_yaw()
             return SE2Pose(x=self.x, y=self.y, angle=se2_angle)
     
    +    @staticmethod
    +    def interp(a, b, fraction):
    +        """
    +        Performs a blend of two SE3Poses.  Out = a * (1 - fraction) + b * fraction
    +
    +        Args:
    +            a(SE3Pose): Lower blend input.
    +            b(SE3Pose): Upper blend input.
    +            fraction(float): The blending factor.  Should be inside [0, 1]
    +        Returns:
    +            SE3Pose
    +        """
    +        x = a.x * (1.0 - fraction) + b.x * fraction
    +        y = a.y * (1.0 - fraction) + b.y * fraction
    +        z = a.z * (1.0 - fraction) + b.z * fraction
    +        rot = Quat.slerp(a.rot, b.rot, fraction)
    +        return SE3Pose(x, y, z, rot)
    +
     
     class Quat(object):
         """Class representing a Quaternion."""
    @@ -591,25 +613,22 @@ def _from_matrix_w(rot):
         def _from_matrix_x(rot):
             """Computes the value of the quaternion for the x-axis from a numpy 3x3 rotation matrix."""
             x = math.sqrt(1 + rot[0, 0] - rot[1, 1] - rot[2, 2]) * 0.5
    -        return Quat(
    -            w=(rot[2, 1] - rot[1, 2]) / (4.0 * x), x=x, y=(rot[0, 1] + rot[1, 0]) / (4.0 * x),
    -            z=(rot[0, 2] + rot[2, 0]) / (4.0 * x))
    +        return Quat(w=(rot[2, 1] - rot[1, 2]) / (4.0 * x), x=x,
    +                    y=(rot[0, 1] + rot[1, 0]) / (4.0 * x), z=(rot[0, 2] + rot[2, 0]) / (4.0 * x))
     
         @staticmethod
         def _from_matrix_y(rot):
             """Computes the value of the quaternion for the y-axis from a numpy 3x3 rotation matrix."""
             y = math.sqrt(1 - rot[0, 0] + rot[1, 1] - rot[2, 2]) * 0.5
    -        return Quat(
    -            w=(rot[0, 2] - rot[2, 0]) / (4.0 * y), x=(rot[0, 1] + rot[1, 0]) / (4.0 * y), y=y,
    -            z=(rot[1, 2] + rot[2, 1]) / (4.0 * y))
    +        return Quat(w=(rot[0, 2] - rot[2, 0]) / (4.0 * y), x=(rot[0, 1] + rot[1, 0]) / (4.0 * y),
    +                    y=y, z=(rot[1, 2] + rot[2, 1]) / (4.0 * y))
     
         @staticmethod
         def _from_matrix_z(rot):
             """Computes the value of the quaternion for the z-axis from a numpy 3x3 rotation matrix."""
             z = math.sqrt(1 - rot[0, 0] - rot[1, 1] + rot[2, 2]) * 0.5
    -        return Quat(
    -            w=(rot[1, 0] - rot[0, 1]) / (4.0 * z), x=(rot[0, 2] + rot[2, 0]) / (4.0 * z),
    -            y=(rot[1, 2] + rot[2, 1]) / (4.0 * z), z=z)
    +        return Quat(w=(rot[1, 0] - rot[0, 1]) / (4.0 * z), x=(rot[0, 2] + rot[2, 0]) / (4.0 * z),
    +                    y=(rot[1, 2] + rot[2, 1]) / (4.0 * z), z=z)
     
         @staticmethod
         def from_roll(angle):
    @@ -685,17 +704,23 @@ def to_proto(self):
     
         def mult(self, other_quat):
             """Computes the multiplication of two math_helpers.Quats."""
    -        return Quat(self.w * other_quat.w - self.x * other_quat.x - self.y * other_quat.y - self.z * other_quat.z,
    -                    self.w * other_quat.x + self.x * other_quat.w + self.y * other_quat.z - self.z * other_quat.y,
    -                    self.w * other_quat.y - self.x * other_quat.z + self.y * other_quat.w + self.z * other_quat.x,
    -                    self.w * other_quat.z + self.x * other_quat.y - self.y * other_quat.x + self.z * other_quat.w)
    +        return Quat(
    +            self.w * other_quat.w - self.x * other_quat.x - self.y * other_quat.y -
    +            self.z * other_quat.z, self.w * other_quat.x + self.x * other_quat.w +
    +            self.y * other_quat.z - self.z * other_quat.y, self.w * other_quat.y -
    +            self.x * other_quat.z + self.y * other_quat.w + self.z * other_quat.x,
    +            self.w * other_quat.z + self.x * other_quat.y - self.y * other_quat.x +
    +            self.z * other_quat.w)
     
         def __mul__(self, other_quat):
             """Overrides the '*' symbol to compute the multiplication between two math_helpers.Quats."""
    -        return Quat(self.w * other_quat.w - self.x * other_quat.x - self.y * other_quat.y - self.z * other_quat.z,
    -                    self.w * other_quat.x + self.x * other_quat.w + self.y * other_quat.z - self.z * other_quat.y,
    -                    self.w * other_quat.y - self.x * other_quat.z + self.y * other_quat.w + self.z * other_quat.x,
    -                    self.w * other_quat.z + self.x * other_quat.y - self.y * other_quat.x + self.z * other_quat.w)
    +        return Quat(
    +            self.w * other_quat.w - self.x * other_quat.x - self.y * other_quat.y -
    +            self.z * other_quat.z, self.w * other_quat.x + self.x * other_quat.w +
    +            self.y * other_quat.z - self.z * other_quat.y, self.w * other_quat.y -
    +            self.x * other_quat.z + self.y * other_quat.w + self.z * other_quat.x,
    +            self.w * other_quat.z + self.x * other_quat.y - self.y * other_quat.x +
    +            self.z * other_quat.w)
     
         def normalize(self):
             """Normalizes the quaternion."""
    @@ -715,12 +740,45 @@ def closest_yaw_only_quaternion(self):
             """Computes a yaw-only math_helpers.Quat from the current roll/pitch/yaw math_helpers.Quat"""
             mag = math.sqrt(self.w * self.w + self.z * self.z)
             if mag > 0:
    -            return Quat(w=self.w/mag, x=0, y=0, z=self.z/mag)
    +            return Quat(w=self.w / mag, x=0, y=0, z=self.z / mag)
             else:
                 # If the problem is ill posed (i.e. z-axis of quaternion is [0, 0, -1], then preserve old
                 # behavior and always rotate 180 degrees around the y-axis.
                 return Quat(w=0, x=0, y=1, z=0)
     
    +    @staticmethod
    +    def slerp(a, b, fraction):
    +        v0 = numpy.array([a.w, a.x, a.y, a.z])
    +        v1 = numpy.array([b.w, b.x, b.y, b.z])
    +        dot = numpy.dot(v0.transpose(), v1)
    +        # If the dot product is negative, slerp will not take
    +        # the shorter path. Note that v1 and -v1 are equivalent when
    +        # the negation is applied to all four components. Fix by
    +        # reversing one quaternion.
    +        if dot < 0.0:
    +            v0 *= -1.0
    +            dot = -dot
    +
    +        DOT_THRESHOLD = 1.0 - 1e-4
    +        if dot > DOT_THRESHOLD:
    +            # If the inputs are too close for comfort, linearly interpolate
    +            # and normalize the result.
    +            result = v0 + fraction * (v1 - v0)
    +            result /= numpy.sqrt(numpy.dot(result.transpose(), result))
    +        else:
    +            # Since dot is in range [0, DOT_THRESHOLD], acos is safe
    +            theta_0 = math.acos(dot)  # theta_0 = angle between input vectors
    +            theta = theta_0 * fraction  # theta = angle between v0 and result
    +            sin_theta = math.sin(theta)  # compute this value only once
    +            sin_theta_0 = math.sin(theta_0)  # compute this value only once
    +
    +            s0 = math.cos(
    +                theta) - dot * sin_theta / sin_theta_0  # == sin(theta_0 - theta) / sin(theta_0)
    +            s1 = sin_theta / sin_theta_0
    +
    +            result = (s0 * v0) + (s1 * v1)
    +        return Quat(result[0], result[1], result[2], result[3])
    +
     
     def pose_to_xyz_yaw(A_tform_B):
         """Gets the x,y,z yaw of B in A from the SE3Pose protobuf message."""
    @@ -738,24 +796,29 @@ def is_within_threshold(pose_3d, max_translation_meters, max_yaw_degrees):
         angle_deg = math.degrees(abs(delta.angle))
         return (dist_2d < max_translation_meters) and (angle_deg < max_yaw_degrees)
     
    +
     def recenter_angle(q, lower_limit, upper_limit):
         recenter_range = upper_limit - lower_limit
    -    while q >= upper_limit: q -= recenter_range
    -    while q < lower_limit:  q += recenter_range
    +    while q >= upper_limit:
    +        q -= recenter_range
    +    while q < lower_limit:
    +        q += recenter_range
         return q
     
    +
     def skew_matrix_3d(vec3_proto):
         """Creates a 3x3 numpy matrix representing the skew symmetric matrix for a vec3.
            These are used to create the adjoint matrices for SE3Pose's, among other things."""
    -    return numpy.array([[0, -vec3_proto.z, vec3_proto.y],
    -                    [vec3_proto.z, 0, -vec3_proto.x],
    -                    [-vec3_proto.y, vec3_proto.x, 0]])
    +    return numpy.array([[0, -vec3_proto.z, vec3_proto.y], [vec3_proto.z, 0, -vec3_proto.x],
    +                        [-vec3_proto.y, vec3_proto.x, 0]])
    +
     
     def skew_matrix_2d(vec2_proto):
         """Creates a 2x1 numpy matrix representing the skew symmetric matrix for a vec2.
            These are used to create the adjoint matrices for SE2Pose's, among other things."""
         return numpy.array([[vec2_proto.y, -vec2_proto.x]])
     
    +
     def transform_se2velocity(a_adjoint_b_matrix, se2_velocity_in_b):
         """
         Changes the frame that the SE(2) Velocity is expressed in. More specifically, it converts the
    @@ -774,10 +837,12 @@ def transform_se2velocity(a_adjoint_b_matrix, se2_velocity_in_b):
             se2_velocity_in_b_vector = se2_velocity_in_b.to_vector()
         else:
             return None
    -    se2_velocity_in_a_vector = numpy.asarray(numpy.matmul(a_adjoint_b_matrix, se2_velocity_in_b_vector))
    +    se2_velocity_in_a_vector = numpy.asarray(
    +        numpy.matmul(a_adjoint_b_matrix, se2_velocity_in_b_vector))
         se2_velocity_in_a = SE2Velocity.from_vector(se2_velocity_in_a_vector)
         return se2_velocity_in_a
     
    +
     def transform_se3velocity(a_adjoint_b_matrix, se3_velocity_in_b):
         """
         Changes the frame that the SE(3) Velocity is expressed in. More specifically, it converts the
    diff --git a/python/bosdyn-client/src/bosdyn/client/network_compute_bridge_client.py b/python/bosdyn-client/src/bosdyn/client/network_compute_bridge_client.py
    index 6aaf656b2..7ffb460de 100644
    --- a/python/bosdyn-client/src/bosdyn/client/network_compute_bridge_client.py
    +++ b/python/bosdyn-client/src/bosdyn/client/network_compute_bridge_client.py
    @@ -16,15 +16,19 @@
     from bosdyn.api import network_compute_bridge_service_pb2
     from bosdyn.api import network_compute_bridge_service_pb2_grpc
     
    +
     class ExternalServiceNotFoundError(ResponseError):
         """The requested service for external computation was not found in the directory."""
     
    +
     class ExternalServerError(ResponseError):
         """The call to the external server did not complete successfully."""
     
    +
     class NetworkComputeRotationError(ResponseError):
         """The robot failed to rotate the image as requested."""
     
    +
     class NetworkComputeBridgeClient(BaseClient):
         """Client to either the NetworkComputeBridgeService or the NetworkComputeBridgeWorkerService."""
     
    @@ -52,11 +56,13 @@ def list_available_models_command(self, list_request, **kwargs):
                 ExternalServerError: Either the service or worker service threw an error when responding with
                     the set of all models.
             """
    -        return self.call(self._stub.ListAvailableModels, list_request, None, _list_available_models_error, **kwargs)
    +        return self.call(self._stub.ListAvailableModels, list_request, None,
    +                         _list_available_models_error, **kwargs)
     
         def list_available_models_command_async(self, list_request, **kwargs):
             """Async version of list_available_models_command()."""
    -        return self.call_async(self._stub.ListAvailableModels, list_request, None, _list_available_models_error, **kwargs)
    +        return self.call_async(self._stub.ListAvailableModels, list_request, None,
    +                               _list_available_models_error, **kwargs)
     
         def network_compute_bridge_command(self, network_compute_request, **kwargs):
             """Issue the main network compute bridge request to run a model on specific, requested data.
    @@ -78,11 +84,14 @@ def network_compute_bridge_command(self, network_compute_request, **kwargs):
                     image as requested.
     
             """
    -        return self.call(self._stub.NetworkCompute, network_compute_request, None, _network_compute_error, **kwargs)
    +        return self.call(self._stub.NetworkCompute, network_compute_request, None,
    +                         _network_compute_error, **kwargs)
     
         def network_compute_bridge_command_async(self, network_compute_request, **kwargs):
             """Async version of network_compute_bridge_command()."""
    -        return self.call_async(self._stub.NetworkCompute, network_compute_request, None, _network_compute_error, **kwargs)
    +        return self.call_async(self._stub.NetworkCompute, network_compute_request, None,
    +                               _network_compute_error, **kwargs)
    +
     
     @handle_common_header_errors
     def _network_compute_error(response):
    @@ -94,14 +103,18 @@ def _network_compute_error(response):
     
         return error_type(response=response, error_message=message)
     
    +
     _NETWORK_COMPUTE_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
     _NETWORK_COMPUTE_STATUS_TO_ERROR.update({
    -    network_compute_bridge_pb2.NETWORK_COMPUTE_STATUS_UNKNOWN: error_pair(UnsetStatusError),
    +    network_compute_bridge_pb2.NETWORK_COMPUTE_STATUS_UNKNOWN:
    +        error_pair(UnsetStatusError),
         network_compute_bridge_pb2.NETWORK_COMPUTE_STATUS_SUCCESS: (None, None),
    -    network_compute_bridge_pb2.NETWORK_COMPUTE_STATUS_EXTERNAL_SERVICE_NOT_FOUND: (ExternalServiceNotFoundError,
    -                                             ExternalServiceNotFoundError.__doc__),
    -    network_compute_bridge_pb2.NETWORK_COMPUTE_STATUS_EXTERNAL_SERVER_ERROR: (ExternalServerError, None),
    -    network_compute_bridge_pb2.NETWORK_COMPUTE_STATUS_ROTATION_ERROR: (NetworkComputeRotationError, None),
    +    network_compute_bridge_pb2.NETWORK_COMPUTE_STATUS_EXTERNAL_SERVICE_NOT_FOUND:
    +        (ExternalServiceNotFoundError, ExternalServiceNotFoundError.__doc__),
    +    network_compute_bridge_pb2.NETWORK_COMPUTE_STATUS_EXTERNAL_SERVER_ERROR:
    +        (ExternalServerError, None),
    +    network_compute_bridge_pb2.NETWORK_COMPUTE_STATUS_ROTATION_ERROR:
    +        (NetworkComputeRotationError, None),
     })
     
     
    @@ -115,11 +128,14 @@ def _list_available_models_error(response):
     
         return error_type(response=response, error_message=message)
     
    +
     _LIST_AVAILABLE_MODELS_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
     _LIST_AVAILABLE_MODELS_STATUS_TO_ERROR.update({
    -    network_compute_bridge_pb2.LIST_AVAILABLE_MODELS_STATUS_UNKNOWN: error_pair(UnsetStatusError),
    +    network_compute_bridge_pb2.LIST_AVAILABLE_MODELS_STATUS_UNKNOWN:
    +        error_pair(UnsetStatusError),
         network_compute_bridge_pb2.LIST_AVAILABLE_MODELS_STATUS_SUCCESS: (None, None),
    -    network_compute_bridge_pb2.LIST_AVAILABLE_MODELS_STATUS_EXTERNAL_SERVICE_NOT_FOUND: (ExternalServiceNotFoundError,
    -                                             ExternalServiceNotFoundError.__doc__),
    -    network_compute_bridge_pb2.LIST_AVAILABLE_MODELS_STATUS_EXTERNAL_SERVER_ERROR: (ExternalServerError, None),
    +    network_compute_bridge_pb2.LIST_AVAILABLE_MODELS_STATUS_EXTERNAL_SERVICE_NOT_FOUND:
    +        (ExternalServiceNotFoundError, ExternalServiceNotFoundError.__doc__),
    +    network_compute_bridge_pb2.LIST_AVAILABLE_MODELS_STATUS_EXTERNAL_SERVER_ERROR:
    +        (ExternalServerError, None),
     })
    diff --git a/python/bosdyn-client/src/bosdyn/client/payload_registration.py b/python/bosdyn-client/src/bosdyn/client/payload_registration.py
    index 2df4f2c00..812ff919f 100644
    --- a/python/bosdyn-client/src/bosdyn/client/payload_registration.py
    +++ b/python/bosdyn-client/src/bosdyn/client/payload_registration.py
    @@ -118,8 +118,12 @@ def update_payload_version(self, guid, secret, updated_version, **kw_args):
               PayloadRegistrationResponseError: Something went wrong during the payload registration.
             """
             request = payload_registration_protos.UpdatePayloadVersionRequest()
    +        # Deprecated credential fields.
             request.payload_guid = guid
             request.payload_secret = secret
    +        # Supported credential fields for 2.4+ robots.
    +        request.payload_credentials.guid = guid
    +        request.payload_credentials.secret = secret
             request.updated_version.CopyFrom(updated_version)
             return self.call(self._stub.UpdatePayloadVersion, request,
                              error_from_response=_update_payload_version_error, **kw_args)
    @@ -140,11 +144,15 @@ def update_payload_version_async(self, guid, secret, updated_version, **kw_args)
               PayloadRegistrationResponseError: Something went wrong during the payload registration.
             """
             request = payload_registration_protos.UpdatePayloadVersionRequest()
    +        # Deprecated credential fields.
             request.payload_guid = guid
             request.payload_secret = secret
    +        # Supported credential fields for 2.4+ robots.
    +        request.payload_credentials.guid = guid
    +        request.payload_credentials.secret = secret
             request.updated_version.CopyFrom(updated_version)
             return self.call_async(self._stub.UpdatePayloadVersion, request,
    -                         error_from_response=_update_payload_version_error, **kw_args)
    +                               error_from_response=_update_payload_version_error, **kw_args)
     
         def get_payload_auth_token(self, guid, secret, **kw_args):
             """Request a limited-access auth token for a payload.
    @@ -167,18 +175,112 @@ def get_payload_auth_token(self, guid, secret, **kw_args):
                 does not match any existing payload.
               PayloadRegistrationResponseError: Something went wrong during the payload registration.
             """
    -        request = payload_registration_protos.GetPayloadAuthTokenRequest(
    -            payload_guid=guid, payload_secret=secret)
    +        request = payload_registration_protos.GetPayloadAuthTokenRequest()
    +        # Deprecated credential fields.
    +        request.payload_guid = guid
    +        request.payload_secret = secret
    +        # Supported credential fields for 2.4+ robots.
    +        request.payload_credentials.guid = guid
    +        request.payload_credentials.secret = secret
    +
             return self.call(self._stub.GetPayloadAuthToken, request, value_from_response=_get_token,
                              error_from_response=_get_payload_auth_token_error, **kw_args)
     
    +    def attach_payload(self, guid, secret, **kw_args):
    +        """Attach a payload to the robot.
    +
    +        Args:
    +          guid:                 The GUID of the payload to attach.
    +          secret:               Secret of the payload to attach.
    +          kw_args:              Extra arguments to pass to grpc call invocation.
    +
    +        Raises:
    +          RpcError: Problem communicating with the robot.
    +          PayloadDoesNotExistError: A payload with the provided GUID does not exist.
    +          InvalidPayloadCredentialsError: The GUID + secret does not match an existing payload.
    +          PayloadNotAuthorizedError: The payload you've requested to change is not yet authorized.
    +          PayloadRegistrationResponseError: Something went wrong during the payload registration.
    +        """
    +        request = payload_registration_protos.UpdatePayloadAttachedRequest()
    +        request.payload_credentials.guid = guid
    +        request.payload_credentials.secret = secret
    +        request.request = payload_registration_protos.UpdatePayloadAttachedRequest.REQUEST_ATTACH
    +        return self.call(self._stub.UpdatePayloadAttached, request,
    +                         error_from_response=_update_payload_attached_error, **kw_args)
    +
    +    def attach_payload_async(self, guid, secret, **kw_args):
    +        """Attach a payload to the robot.
    +
    +        Args:
    +          guid:                 The GUID of the payload to attach.
    +          secret:               Secret of the payload to attach.
    +          kw_args:              Extra arguments to pass to grpc call invocation.
    +
    +        Raises:
    +          RpcError: Problem communicating with the robot.
    +          PayloadDoesNotExistError: A payload with the provided GUID does not exist.
    +          InvalidPayloadCredentialsError: The GUID + secret does not match an existing payload.
    +          PayloadNotAuthorizedError: The payload you've requested to change is not yet authorized.
    +          PayloadRegistrationResponseError: Something went wrong during the payload registration.
    +        """
    +        request = payload_registration_protos.UpdatePayloadAttachedRequest()
    +        request.payload_credentials.guid = guid
    +        request.payload_credentials.secret = secret
    +        request.request = payload_registration_protos.UpdatePayloadAttachedRequest.REQUEST_ATTACH
    +        return self.call_async(self._stub.UpdatePayloadAttached, request,
    +                               error_from_response=_update_payload_attached_error, **kw_args)
    +
    +    def detach_payload(self, guid, secret, **kw_args):
    +        """Detach a payload from the robot.
    +
    +        Args:
    +          guid:                 The GUID of the payload to detach.
    +          secret:               Secret of the payload to detach.
    +          kw_args:              Extra arguments to pass to grpc call invocation.
    +
    +        Raises:
    +          RpcError: Problem communicating with the robot.
    +          PayloadDoesNotExistError: A payload with the provided GUID does not exist.
    +          InvalidPayloadCredentialsError: The GUID + secret does not match an existing payload.
    +          PayloadNotAuthorizedError: The payload you've requested to change is not yet authorized.
    +          PayloadRegistrationResponseError: Something went wrong during the payload registration.
    +        """
    +        request = payload_registration_protos.UpdatePayloadAttachedRequest()
    +        request.payload_credentials.guid = guid
    +        request.payload_credentials.secret = secret
    +        request.request = payload_registration_protos.UpdatePayloadAttachedRequest.REQUEST_DETACH
    +        return self.call(self._stub.UpdatePayloadAttached, request,
    +                         error_from_response=_update_payload_attached_error, **kw_args)
    +
    +    def detach_payload_async(self, guid, secret, **kw_args):
    +        """Detach a payload from the robot.
    +
    +        Args:
    +          guid:                 The GUID of the payload to detach.
    +          secret:               Secret of the payload to detach.
    +          kw_args:              Extra arguments to pass to grpc call invocation.
    +
    +        Raises:
    +          RpcError: Problem communicating with the robot.
    +          PayloadDoesNotExistError: A payload with the provided GUID does not exist.
    +          InvalidPayloadCredentialsError: The GUID + secret does not match an existing payload.
    +          PayloadNotAuthorizedError: The payload you've requested to change is not yet authorized.
    +          PayloadRegistrationResponseError: Something went wrong during the payload registration.
    +        """
    +        request = payload_registration_protos.UpdatePayloadAttachedRequest()
    +        request.payload_credentials.guid = guid
    +        request.payload_credentials.secret = secret
    +        request.request = payload_registration_protos.UpdatePayloadAttachedRequest.REQUEST_DETACH
    +        return self.call_async(self._stub.UpdatePayloadAttached, request,
    +                               error_from_response=_update_payload_attached_error, **kw_args)
    +
     
     # Associate proto status errors to python client errors for RegisterPayload
     _REGISTER_PAYLOAD_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
     _REGISTER_PAYLOAD_STATUS_TO_ERROR.update({
         payload_registration_protos.RegisterPayloadResponse.STATUS_OK: (None, None),
         payload_registration_protos.RegisterPayloadResponse.STATUS_ALREADY_EXISTS:
    -    (PayloadAlreadyExistsError, PayloadAlreadyExistsError.__doc__),
    +        (PayloadAlreadyExistsError, PayloadAlreadyExistsError.__doc__),
     })
     
     
    @@ -198,9 +300,9 @@ def _payload_registration_error(response):
     _UPDATE_PAYLOAD_VERSION_STATUS_TO_ERROR.update({
         payload_registration_protos.UpdatePayloadVersionResponse.STATUS_OK: (None, None),
         payload_registration_protos.UpdatePayloadVersionResponse.STATUS_DOES_NOT_EXIST:
    -    (PayloadDoesNotExistError, PayloadDoesNotExistError.__doc__),
    +        (PayloadDoesNotExistError, PayloadDoesNotExistError.__doc__),
         payload_registration_protos.UpdatePayloadVersionResponse.STATUS_INVALID_CREDENTIALS:
    -    (InvalidPayloadCredentialsError, InvalidPayloadCredentialsError.__doc__),
    +        (InvalidPayloadCredentialsError, InvalidPayloadCredentialsError.__doc__),
     })
     
     
    @@ -220,9 +322,9 @@ def _update_payload_version_error(response):
     _GET_PAYLOAD_AUTH_TOKEN_STATUS_TO_ERROR.update({
         payload_registration_protos.GetPayloadAuthTokenResponse.STATUS_OK: (None, None),
         payload_registration_protos.GetPayloadAuthTokenResponse.STATUS_INVALID_CREDENTIALS:
    -    (InvalidPayloadCredentialsError, InvalidPayloadCredentialsError.__doc__),
    +        (InvalidPayloadCredentialsError, InvalidPayloadCredentialsError.__doc__),
         payload_registration_protos.GetPayloadAuthTokenResponse.STATUS_PAYLOAD_NOT_AUTHORIZED:
    -    (PayloadNotAuthorizedError, PayloadNotAuthorizedError.__doc__),
    +        (PayloadNotAuthorizedError, PayloadNotAuthorizedError.__doc__),
     })
     
     
    @@ -236,6 +338,29 @@ def _get_payload_auth_token_error(response):
             status_to_string=payload_registration_protos.GetPayloadAuthTokenResponse.Status.Name,
             status_to_error=_GET_PAYLOAD_AUTH_TOKEN_STATUS_TO_ERROR)
     
    +# Associate proto status errors to python client errors for UpdatePayloadAttachedResponse
    +_UPDATE_PAYLOAD_ATTACHED_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
    +_UPDATE_PAYLOAD_ATTACHED_STATUS_TO_ERROR.update({
    +    payload_registration_protos.UpdatePayloadAttachedResponse.STATUS_OK: (None, None),
    +    payload_registration_protos.UpdatePayloadAttachedResponse.STATUS_DOES_NOT_EXIST:
    +        (PayloadDoesNotExistError, PayloadDoesNotExistError.__doc__),
    +    payload_registration_protos.UpdatePayloadAttachedResponse.STATUS_INVALID_CREDENTIALS:
    +        (InvalidPayloadCredentialsError, InvalidPayloadCredentialsError.__doc__),
    +    payload_registration_protos.UpdatePayloadAttachedResponse.STATUS_PAYLOAD_NOT_AUTHORIZED:
    +        (PayloadNotAuthorizedError, PayloadNotAuthorizedError.__doc__),
    +})
    +
    +
    +# Function to parse all types of errors from update payload attached request
    +@handle_common_header_errors
    +@handle_unset_status_error(unset='STATUS_UNKNOWN')
    +def _update_payload_attached_error(response):
    +    """Return a custom exception based on response, None if no error."""
    +    return error_factory(
    +        response, response.status,
    +        status_to_string=payload_registration_protos.UpdatePayloadAttachedResponse.Status.Name,
    +        status_to_error=_UPDATE_PAYLOAD_ATTACHED_STATUS_TO_ERROR)
    +
     
     class PayloadRegistrationKeepAlive(object):
         """Helper class to keep a payload entry registered.
    @@ -291,7 +416,8 @@ def start(self):
                 self.pay_reg_client.register_payload(self.payload, self.secret)
             except PayloadAlreadyExistsError as exc:
                 # If the payload exists, log a warning and continue.
    -            self.logger.warning('Got a "payload already exists" error: %s\nContinuing to start thread.', str(exc))
    +            self.logger.warning(
    +                'Got a "payload already exists" error: %s\nContinuing to start thread.', str(exc))
             else:
                 self.logger.info('Payload registered.')
     
    diff --git a/python/bosdyn-client/src/bosdyn/client/point_cloud.py b/python/bosdyn-client/src/bosdyn/client/point_cloud.py
    index 3bec50024..41eacb79e 100644
    --- a/python/bosdyn-client/src/bosdyn/client/point_cloud.py
    +++ b/python/bosdyn-client/src/bosdyn/client/point_cloud.py
    @@ -17,11 +17,13 @@
     import bosdyn.api.point_cloud_service_pb2_grpc as point_cloud_service
     
     from .common import BaseClient
    -from bosdyn.client.common import (error_factory, error_pair, common_header_errors, handle_common_header_errors)
    +from bosdyn.client.common import (error_factory, error_pair, common_header_errors,
    +                                  handle_common_header_errors)
     from bosdyn.client.exceptions import ResponseError, UnsetStatusError
     
     LOGGER = logging.getLogger('point_cloud_client')
     
    +
     class PointCloudResponseError(ResponseError):
         """General class of errors for PointCloud service."""
     
    @@ -41,12 +43,17 @@ class PointCloudDataError(PointCloudResponseError):
     _STATUS_TO_ERROR = collections.defaultdict(lambda: (PointCloudResponseError, None))
     _STATUS_TO_ERROR.update({
         point_cloud_protos.PointCloudResponse.STATUS_OK: (None, None),
    -    point_cloud_protos.PointCloudResponse.STATUS_UNKNOWN_SOURCE: error_pair(UnknownPointCloudSourceError),
    -    point_cloud_protos.PointCloudResponse.STATUS_SOURCE_DATA_ERROR: error_pair(SourceDataError),
    -    point_cloud_protos.PointCloudResponse.STATUS_UNKNOWN: error_pair(UnsetStatusError),
    -    point_cloud_protos.PointCloudResponse.STATUS_POINT_CLOUD_DATA_ERROR: error_pair(PointCloudDataError),
    +    point_cloud_protos.PointCloudResponse.STATUS_UNKNOWN_SOURCE:
    +        error_pair(UnknownPointCloudSourceError),
    +    point_cloud_protos.PointCloudResponse.STATUS_SOURCE_DATA_ERROR:
    +        error_pair(SourceDataError),
    +    point_cloud_protos.PointCloudResponse.STATUS_UNKNOWN:
    +        error_pair(UnsetStatusError),
    +    point_cloud_protos.PointCloudResponse.STATUS_POINT_CLOUD_DATA_ERROR:
    +        error_pair(PointCloudDataError),
     })
     
    +
     @handle_common_header_errors
     def _error_from_response(response):
         """Return a custom exception based on the first invalid point_cloud response, None if no error."""
    @@ -59,6 +66,7 @@ def _error_from_response(response):
                 return result
         return None
     
    +
     class PointCloudClient(BaseClient):
         """A client handling point clouds."""
     
    @@ -84,8 +92,8 @@ def list_point_cloud_sources(self, **kwargs):
         def list_point_cloud_sources_async(self, **kwargs):
             """Async version of list_point_cloud_sources()"""
             req = self._get_list_point_cloud_source_request()
    -        return self.call_async(self._stub.ListPointCloudSources, req, _list_point_cloud_sources_value,
    -                               common_header_errors, **kwargs)
    +        return self.call_async(self._stub.ListPointCloudSources, req,
    +                               _list_point_cloud_sources_value, common_header_errors, **kwargs)
     
         def get_point_cloud_from_sources(self, point_cloud_sources, **kwargs):
             """Obtain point clouds from sources using default parameters.
    @@ -104,12 +112,13 @@ def get_point_cloud_from_sources(self, point_cloud_sources, **kwargs):
                 UnsetStatusError: An internal PointCloudService issue has happened
                 PointCloudDataError: Problem with the point cloud data. Only PointCloudSource is filled
             """
    -        return self.get_point_cloud([build_pc_request(src) for src in point_cloud_sources], **kwargs)
    +        return self.get_point_cloud([build_pc_request(src) for src in point_cloud_sources],
    +                                    **kwargs)
     
         def get_point_cloud_from_sources_async(self, point_cloud_sources, **kwargs):
             """Obtain point clouds from sources using default parameters."""
             return self.get_point_cloud_async([build_pc_request(src) for src in point_cloud_sources],
    -                                    **kwargs)
    +                                          **kwargs)
     
         def get_point_cloud(self, point_cloud_requests, **kw_args):
             """Get the most recent point cloud
    @@ -167,6 +176,7 @@ def _get_point_cloud_request(point_cloud_requests):
         def _get_list_point_cloud_source_request():
             return point_cloud_protos.ListPointCloudSourcesRequest()
     
    +
     def build_pc_request(point_cloud_source_name):
         """Helper function which builds an PointCloudRequest from an point cloud source name.
     
    @@ -178,6 +188,7 @@ def build_pc_request(point_cloud_source_name):
         """
         return point_cloud_protos.PointCloudRequest(point_cloud_source_name=point_cloud_source_name)
     
    +
     def _list_point_cloud_sources_value(response):
         return response.point_cloud_sources
     
    diff --git a/python/bosdyn-client/src/bosdyn/client/power.py b/python/bosdyn-client/src/bosdyn/client/power.py
    index a7bcb1b04..02e5a3008 100644
    --- a/python/bosdyn-client/src/bosdyn/client/power.py
    +++ b/python/bosdyn-client/src/bosdyn/client/power.py
    @@ -44,15 +44,15 @@ class CommandInProgressError(PowerResponseError):
     
     
     class EstoppedError(PowerResponseError):
    -    """Cannot power on while estopped.
    +    """Cannot power on while estopped; inspect EStopState for more info."""
     
    -       Inspect EStopState for more info."""
     
    +class OverriddenError(PowerResponseError):
    +    """The command was overridden and is no longer valid."""
     
    -class FaultedError(PowerResponseError):
    -    """Cannot power on due to a fault.
     
    -       Inspect FaultState for more info."""
    +class FaultedError(PowerResponseError):
    +    """Cannot power on due to a fault; inspect FaultState for more info."""
     
     
     class PowerError(Error):
    @@ -169,6 +169,7 @@ def _power_feedback_error_from_response(response):
         power_pb2.STATUS_FAULTED: (FaultedError, FaultedError.__doc__),
         power_pb2.STATUS_INTERNAL_ERROR: (InternalServerError, InternalServerError.__doc__),
         power_pb2.STATUS_LICENSE_ERROR: (LicenseError, LicenseError.__doc__),
    +    power_pb2.STATUS_OVERRIDDEN: (OverriddenError, OverriddenError.__doc__),
     })
     
     
    @@ -176,7 +177,14 @@ def _power_status_from_response(response):
         return response.status
     
     
    +@deprecated(reason='Replaced by the less ambiguous safe_power_off_motors function.', version='3.0.0',
    +            action="ignore")
     def safe_power_off(command_client, state_client, timeout_sec=30, update_frequency=1.0, **kwargs):
    +    """Safely power off motors. See safe_power_off_motors()."""
    +    safe_power_off_motors(command_client, state_client, timeout_sec, update_frequency, **kwargs)
    +
    +
    +def safe_power_off_motors(command_client, state_client, timeout_sec=30, update_frequency=1.0, **kwargs):
         """Power off robot motors safely. This function blocks until robot safely powers off. This
         means the robot will attempt to sit before powering motors off.
     
    @@ -266,9 +274,34 @@ def power_off_motors(power_client, timeout_sec=30, update_frequency=1.0, **kwarg
         _power_command(power_client, request, timeout_sec, update_frequency, **kwargs)
     
     
    +def safe_power_off_robot(command_client, state_client, power_client, timeout_sec=30,
    +                         update_frequency=1.0, **kwargs):
    +    """Power off the robot motors and then the robot computers safely. This function blocks until
    +    robot safely powers off. This means the robot will attempt to sit before powering motors off.
    +
    +    Args:
    +        command_client (RobotCommandClient): client for calling RobotCommandService safe power off.
    +        state_client (RobotStateClient): client for monitoring power state.
    +        power_client (bosdyn.api.PowerClient): client for calling power service.
    +        timeout_sec (float): Max time this function will block for.
    +        update_frequency (float): The frequency with which the robot should check if the command
    +                                  has succeeded.
    +
    +    Raises:
    +        RpcError: Problem communicating with the robot.
    +        power.CommandTimedOutError: Did not power off within timeout_sec
    +        RobotCommandResponseError: Something went wrong with the safe power off.
    +    """
    +    end_time = time.time() + timeout_sec
    +    safe_power_off_motors(command_client, state_client, timeout_sec=end_time - time.time(),
    +                          update_frequency=update_frequency, **kwargs)
    +    power_off_robot(power_client, timeout_sec=end_time - time.time(),
    +                    update_frequency=update_frequency, **kwargs)
    +
    +
     def power_off_robot(power_client, timeout_sec=30, update_frequency=1.0, **kwargs):
         """Fully power off the robot. Powering off the robot will stop API comms.
    -    
    +
         Args:
             power_client (bosdyn.api.PowerClient): client for calling power service.
             timeout_sec (float): Max time this function will block for.
    @@ -284,9 +317,34 @@ def power_off_robot(power_client, timeout_sec=30, update_frequency=1.0, **kwargs
                        **kwargs)
     
     
    +def safe_power_cycle_robot(command_client, state_client, power_client, timeout_sec=30,
    +                           update_frequency=1.0, **kwargs):
    +    """Power cycle the robot safely. This function blocks until robot safely powers off. The robot
    +    will attempt to sit before powering cycling.
    +
    +    Args:
    +        command_client (RobotCommandClient): client for calling RobotCommandService safe power off.
    +        state_client (RobotStateClient): client for monitoring power state.
    +        power_client (bosdyn.api.PowerClient): client for calling power service.
    +        timeout_sec (float): Max time this function will block for.
    +        update_frequency (float): The frequency with which the robot should check if the command
    +                                  has succeeded.
    +
    +    Raises:
    +        RpcError: Problem communicating with the robot.
    +        power.CommandTimedOutError: Did not power off within timeout_sec
    +        RobotCommandResponseError: Something went wrong with the safe power off.
    +    """
    +    end_time = time.time() + timeout_sec
    +    safe_power_off_motors(command_client, state_client, timeout_sec=end_time - time.time(),
    +                          update_frequency=update_frequency, **kwargs)
    +    power_cycle_robot(power_client, timeout_sec=end_time - time.time(),
    +                      update_frequency=update_frequency, **kwargs)
    +
    +
     def power_cycle_robot(power_client, timeout_sec=30, update_frequency=1.0, **kwargs):
         """Power cycle the robot. Power cycling the robot will stop API comms.
    -    
    +
         Args:
             power_client (bosdyn.api.PowerClient): client for calling power service.
             timeout_sec (float): Max time this function will block for.
    @@ -304,7 +362,7 @@ def power_cycle_robot(power_client, timeout_sec=30, update_frequency=1.0, **kwar
     
     def power_off_payload_ports(power_client, timeout_sec=30, update_frequency=1.0, **kwargs):
         """Power off the robot payload ports.
    -    
    +
         Args:
             power_client (bosdyn.api.PowerClient): client for calling power service.
             timeout_sec (float): Max time this function will block for.
    @@ -321,7 +379,7 @@ def power_off_payload_ports(power_client, timeout_sec=30, update_frequency=1.0,
     
     def power_on_payload_ports(power_client, timeout_sec=30, update_frequency=1.0, **kwargs):
         """Power on the robot payload ports.
    -    
    +
         Args:
             power_client (bosdyn.api.PowerClient): client for calling power service.
             timeout_sec (float): Max time this function will block for.
    @@ -338,7 +396,7 @@ def power_on_payload_ports(power_client, timeout_sec=30, update_frequency=1.0, *
     
     def power_off_wifi_radio(power_client, timeout_sec=30, update_frequency=1.0, **kwargs):
         """Power off the robot Wi-Fi radio.
    -    
    +
         Args:
             power_client (bosdyn.api.PowerClient): client for calling power service.
             timeout_sec (float): Max time this function will block for.
    @@ -355,7 +413,7 @@ def power_off_wifi_radio(power_client, timeout_sec=30, update_frequency=1.0, **k
     
     def power_on_wifi_radio(power_client, timeout_sec=30, update_frequency=1.0, **kwargs):
         """Power off the robot Wi-Fi radio.
    -    
    +
         Args:
             power_client (bosdyn.api.PowerClient): client for calling power service.
             timeout_sec (float): Max time this function will block for.
    @@ -377,7 +435,7 @@ def power_on_wifi_radio(power_client, timeout_sec=30, update_frequency=1.0, **kw
     def _power_command(power_client, request, timeout_sec=30, update_frequency=1.0,
                        expect_grpc_timeout=False, **kwargs):
         """Helper function to issue command to power client.
    -    
    +
         Args:
             power_client (bosdyn.api.PowerClient): Client for calling power service.
             request (bosdyn.api.PowerCommandRequest): Request to make to power service.
    diff --git a/python/bosdyn-client/src/bosdyn/client/recording.py b/python/bosdyn-client/src/bosdyn/client/recording.py
    index 2815e2ab2..b776e5c6f 100644
    --- a/python/bosdyn-client/src/bosdyn/client/recording.py
    +++ b/python/bosdyn-client/src/bosdyn/client/recording.py
    @@ -324,6 +324,10 @@ class NotLocalizedToExistingMapError(RecordingServiceResponseError):
         """The robot is not localized to the existing map and cannot start recording."""
     
     
    +class TooFarFromExistingMapError(RecordingServiceResponseError):
    +    """The robot is too far from the existing map and cannot start recording."""
    +
    +
     class RemoteCloudFailureNotInDirectoryError(RecordingServiceResponseError):
         """Failed to start recording because a remote point cloud (e.g. a LIDAR) is not registered to the service directory."""
     
    @@ -375,7 +379,9 @@ def _get_response(response):
         recording_pb2.StartRecordingResponse.STATUS_REMOTE_CLOUD_FAILURE_NO_DATA:
             (RemoteCloudFailureNoDataError, RemoteCloudFailureNoDataError.__doc__),
         recording_pb2.StartRecordingResponse.STATUS_FIDUCIAL_POSE_NOT_OK:
    -        (FiducialPoseError, FiducialPoseError.__doc__)
    +        (FiducialPoseError, FiducialPoseError.__doc__),
    +    recording_pb2.StartRecordingResponse.STATUS_TOO_FAR_FROM_EXISTING_MAP:
    +        (TooFarFromExistingMapError, TooFarFromExistingMapError.__doc__)
     })
     
     
    diff --git a/python/bosdyn-client/src/bosdyn/client/robot.py b/python/bosdyn-client/src/bosdyn/client/robot.py
    index 7cb9fd75d..45ed68ce7 100644
    --- a/python/bosdyn-client/src/bosdyn/client/robot.py
    +++ b/python/bosdyn-client/src/bosdyn/client/robot.py
    @@ -9,20 +9,22 @@
     import logging
     import time
     
    +import bosdyn.api.data_buffer_pb2 as data_buffer_protos
     import bosdyn.client.channel
     from bosdyn.util import timestamp_to_sec
     
     from .auth import AuthClient
     from .channel import DEFAULT_MAX_MESSAGE_LENGTH
     from .data_buffer import DataBufferClient
    -from .data_service import DataServiceClient
    +from .data_buffer import log_event as pkg_log_event
     from .directory import DirectoryClient
     from .directory_registration import DirectoryRegistrationClient
     from .estop import EstopClient
     from .estop import is_estopped as pkg_is_estopped
     from .exceptions import Error
     from .lease import LeaseWallet
    -from .payload_registration import PayloadRegistrationClient, PayloadAlreadyExistsError, PayloadNotAuthorizedError
    +from .payload_registration import (PayloadRegistrationClient, PayloadAlreadyExistsError,
    +                                   PayloadNotAuthorizedError)
     from .power import PowerClient
     from .power import power_on as pkg_power_on
     from .power import power_off as pkg_power_off
    @@ -88,8 +90,6 @@ class Robot(object):
     
         _bootstrap_service_authorities = {
             AuthClient.default_service_name: 'auth.spot.robot',
    -        DataBufferClient.default_service_name: 'buffer.spot.robot',
    -        DataServiceClient.default_service_name: 'data.spot.robot',
             DirectoryClient.default_service_name: 'api.spot.robot',
             DirectoryRegistrationClient.default_service_name: 'api.spot.robot',
             PayloadRegistrationClient.default_service_name: 'payload-registration.spot.robot',
    @@ -132,18 +132,24 @@ def __init__(
             self.max_send_message_length = DEFAULT_MAX_MESSAGE_LENGTH
             self.max_receive_message_length = DEFAULT_MAX_MESSAGE_LENGTH
     
    -    def __del__(self):
    +    def _shutdown(self):
    +        """Shut down background threads for tokens and time sync."""
             if self._time_sync_thread:
                 self._time_sync_thread.stop()
    +            self._time_sync_thread = None
             if self._token_manager:
                 self._token_manager.stop()
    +            self._token_manager = None
     
         def __exit__(self, exc_type, exc_val, exc_tb):
    -        self.__del__()
    +        self._shutdown()
     
         def __enter__(self):
             return self
     
    +    def __del__(self):
    +        self._shutdown()
    +
         def _get_token_id(self, username):
             return '{}.{}'.format(self.serial_number, username)
     
    @@ -265,13 +271,6 @@ def ensure_channel(self, service_name, options=[]):
                 UnregisteredServiceNameError: service_name is unknown.
             """
     
    -        # Update max send/receive message lengths.
    -        if 'grpc.max_receive_message_length' not in [option[0] for option in options]:
    -            options.append(('grpc.max_receive_message_length', self.max_receive_message_length))
    -        if 'grpc.max_send_message_length' not in [option[0] for option in options]:
    -            options.append(('grpc.max_send_message_length', self.max_send_message_length))
    -
    -
             # If a specific channel was not set, look up the authority so we can get a channel.
             # Get the authority from either
             #   1. The bootstrap authority for this client_class, if available
    @@ -297,6 +296,12 @@ def ensure_secure_channel(self, authority, skip_app_token_check=False, options=[
             if authority in self.channels_by_authority:
                 return self.channels_by_authority[authority]
     
    +        # Update max send/receive message lengths.
    +        if 'grpc.max_receive_message_length' not in [option[0] for option in options]:
    +            options.append(('grpc.max_receive_message_length', self.max_receive_message_length))
    +        if 'grpc.max_send_message_length' not in [option[0] for option in options]:
    +            options.append(('grpc.max_send_message_length', self.max_send_message_length))
    +
             if skip_app_token_check:
                 should_send_app_token = False
             else:
    @@ -396,7 +401,7 @@ def authenticate_from_payload_credentials(self, guid, secret, payload_registrati
                 except PayloadNotAuthorizedError:
                     if not printed_warning:
                         printed_warning = True
    -                    print('Payload is not authorized. Authentication will block until an'
    +                    self.logger.warn('Payload is not authorized. Authentication will block until an'
                               ' operator authorizes the payload in the Admin Console.')
                     pass
                 time.sleep(0.1)
    @@ -534,6 +539,26 @@ def operator_comment(self, comment, timestamp_secs=None, timeout=None):
                 robot_timestamp = self.time_sync.robot_timestamp_from_local_secs(timestamp_secs)
             client.add_operator_comment(comment, robot_timestamp=robot_timestamp, timeout=timeout)
     
    +    def log_event(  # pylint: disable=too-many-arguments,no-member
    +            self, event_type, level, description, start_timestamp_secs, end_timestamp_secs=None,
    +            id_str=None, parameters=None,
    +            log_preserve_hint=data_buffer_protos.Event.LOG_PRESERVE_HINT_NORMAL):
    +        """Add an Event to the Data Buffer.
    +
    +        Args:
    +          event_type (string):            The type of event.
    +          level (bosdyn.api.Event.Level): The relative importance of the event.
    +          description (string):           A human-readable description of the event.
    +          start_timestamp_secs (float):   Start of the event, in local time.
    +          end_timestamp_secs (float):     End of the event.  start_timestamp_secs is used if None.
    +          id_str (string):                      Unique id for event.  A uuid is generated if None.
    +          parameters ([bosdyn.api.Parameter]):  Parameters to attach to the event.
    +        """
    +        return pkg_log_event(self, event_type=event_type, level=level, description=description,
    +                             start_timestamp_secs=start_timestamp_secs,
    +                             end_timestamp_secs=end_timestamp_secs, id_str=id_str,
    +                             parameters=parameters, log_preserve_hint=log_preserve_hint)
    +
         def power_on(self, timeout_sec=20, update_frequency=1.0, timeout=None):
             """Power on robot. This function blocks until robot powers on.
     
    @@ -648,3 +673,4 @@ def has_arm(self, timeout=None):
             self._has_arm = pkg_has_arm(state_client, timeout=timeout)
             return self._has_arm
     
    +
    diff --git a/python/bosdyn-client/src/bosdyn/client/robot_command.py b/python/bosdyn-client/src/bosdyn/client/robot_command.py
    index 27f9263f4..de45c9897 100644
    --- a/python/bosdyn-client/src/bosdyn/client/robot_command.py
    +++ b/python/bosdyn-client/src/bosdyn/client/robot_command.py
    @@ -9,7 +9,7 @@
     import time
     
     from deprecated.sphinx import deprecated
    -from google.protobuf import any_pb2
    +from google.protobuf import any_pb2, wrappers_pb2
     from bosdyn import geometry
     
     from bosdyn.api import geometry_pb2
    @@ -32,7 +32,7 @@
     from .exceptions import ResponseError, InvalidRequestError, TimedOutError, UnsetStatusError
     from .exceptions import Error as BaseError
     from .frame_helpers import BODY_FRAME_NAME, ODOM_FRAME_NAME, get_se2_a_tform_b
    -from .math_helpers import SE2Pose
    +from .math_helpers import SE2Pose, SE3Pose
     from .lease import add_lease_wallet_processors
     
     # The angles (in radians) that represent the claw gripper open and closed positions.
    @@ -63,9 +63,11 @@ class TooDistantError(RobotCommandResponseError):
     class NotPoweredOnError(RobotCommandResponseError):
         """The robot must be powered on to accept a command."""
     
    +
     class BehaviorFaultError(RobotCommandResponseError):
         """The robot may not be commanded with uncleared behavior faults."""
     
    +
     class NotClearedError(RobotCommandResponseError):
         """Behavior fault could not be cleared."""
     
    @@ -81,6 +83,7 @@ class CommandFailedError(Error):
     class CommandTimedOutError(Error):
         """Timed out waiting for SUCCESS response from robot command."""
     
    +
     class UnknownFrameError(RobotCommandResponseError):
         """Robot does not know how to handle supplied frame."""
     
    @@ -177,7 +180,7 @@ def robot_timestamp_from_local_secs(self, end_time_secs):
                     }
                 }
             },
    -        'gripper_command':{
    +        'gripper_command': {
                 '@command': {
                     'claw_gripper_command': {
                         'trajectory': {
    @@ -238,7 +241,8 @@ def _edit_proto(proto, edit_tree, edit_fn):
                 # Recursion into a one-of message. '@key' means field 'key' contains a one-of message.
                 which_oneof = proto.WhichOneof(key[1:])
                 if not which_oneof or which_oneof not in subtree:
    -                return  # No submessage, or tree doesn't contain a conversion for it.
    +                # No submessage, or tree doesn't contain a conversion for it.
    +                return
                 _edit_proto(getattr(proto, which_oneof), subtree[which_oneof], edit_fn)
             elif subtree:
                 # Recursion into a sub-message by field name.
    @@ -452,7 +456,8 @@ def _set_end_time(key, proto):
             def _to_robot_time(key, proto):
                 """If proto has a field named key with a timestamp, convert timestamp to robot time."""
                 if not (key in proto.DESCRIPTOR.fields_by_name and proto.HasField(key)):
    -                return  # No such field in proto, or field does not contain a timestamp.
    +                # No such field in proto, or field does not contain a timestamp.
    +                return
                 timestamp = getattr(proto, key)
                 converter.convert_timestamp_from_local_to_robot(timestamp)
     
    @@ -503,7 +508,8 @@ def _robot_command_value(response):
     
     
     # yapf: disable
    -_ROBOT_COMMAND_STATUS_TO_ERROR = collections.defaultdict(lambda: (RobotCommandResponseError, None))
    +_ROBOT_COMMAND_STATUS_TO_ERROR = collections.defaultdict(
    +    lambda: (RobotCommandResponseError, None))
     _ROBOT_COMMAND_STATUS_TO_ERROR.update({
         robot_command_pb2.RobotCommandResponse.STATUS_OK: (None, None),
         robot_command_pb2.RobotCommandResponse.STATUS_INVALID_REQUEST: error_pair(InvalidRequestError),
    @@ -564,7 +570,8 @@ def _clear_behavior_fault_value(response):
     
     
     # yapf: disable
    -_CLEAR_BEHAVIOR_FAULT_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
    +_CLEAR_BEHAVIOR_FAULT_STATUS_TO_ERROR = collections.defaultdict(
    +    lambda: (ResponseError, None))
     _CLEAR_BEHAVIOR_FAULT_STATUS_TO_ERROR.update({
         robot_command_pb2.ClearBehaviorFaultResponse.STATUS_CLEARED: (None, None),
         robot_command_pb2.ClearBehaviorFaultResponse.STATUS_NOT_CLEARED:
    @@ -672,6 +679,31 @@ def safe_power_off_command():
                 safe_power_off_request=basic_command_pb2.SafePowerOffCommand.Request())
             command = robot_command_pb2.RobotCommand(full_body_command=full_body_command)
             return command
    +
    +    @staticmethod
    +    def constrained_manipulation_command(task_type, init_wrench_direction_in_frame_name,
    +                                         force_limit, torque_limit, frame_name,
    +                                         tangential_speed=None, rotational_speed=None):
    +        """Command constrained manipulation. """
    +        if (tangential_speed is not None):
    +            full_body_command = full_body_command_pb2.FullBodyCommand.Request(
    +                constrained_manipulation_request=basic_command_pb2.ConstrainedManipulationCommand.
    +                Request(task_type=task_type,
    +                        init_wrench_direction_in_frame_name=init_wrench_direction_in_frame_name,
    +                        frame_name=frame_name, tangential_speed=tangential_speed))
    +        elif (rotational_speed is not None):
    +            full_body_command = full_body_command_pb2.FullBodyCommand.Request(
    +                constrained_manipulation_request=basic_command_pb2.ConstrainedManipulationCommand.
    +                Request(task_type=task_type,
    +                        init_wrench_direction_in_frame_name=init_wrench_direction_in_frame_name,
    +                        frame_name=frame_name, rotational_speed=rotational_speed))
    +        else:
    +            raise Exception("Need either translational or rotational speed")
    +        full_body_command.constrained_manipulation_request.force_limit.value = force_limit
    +        full_body_command.constrained_manipulation_request.torque_limit.value = torque_limit
    +        command = robot_command_pb2.RobotCommand(full_body_command=full_body_command)
    +        return command
    +
         ###################################
         # Mobility commands  - DEPRECATED #
         ###################################
    @@ -716,11 +748,8 @@ def trajectory_command(goal_x, goal_y, goal_heading, frame_name, params=None, bo
             return command
     
         @staticmethod
    -    @deprecated(
    -        reason='Mobility commands are now sent as a part of synchronized commands. '\
    -               'Use synchro_velocity_command instead.',
    -        version='2.1.0',
    -        action="always")
    +    @deprecated(reason='Mobility commands are now sent as a part of synchronized commands. '
    +                'Use synchro_velocity_command instead.', version='2.1.0', action="always")
         def velocity_command(v_x, v_y, v_rot, params=None, body_height=0.0,
                              locomotion_hint=spot_command_pb2.HINT_AUTO, frame_name=BODY_FRAME_NAME):
             """
    @@ -757,11 +786,8 @@ def velocity_command(v_x, v_y, v_rot, params=None, body_height=0.0,
             return command
     
         @staticmethod
    -    @deprecated(
    -        reason='Mobility commands are now sent as a part of synchronized commands. '\
    -               'Use synchro_stand_command instead.',
    -        version='2.1.0',
    -        action="always")
    +    @deprecated(reason='Mobility commands are now sent as a part of synchronized commands. '
    +                'Use synchro_stand_command instead.', version='2.1.0', action="always")
         def stand_command(params=None, body_height=0.0, footprint_R_body=geometry.EulerZXY()):
             """
             Command robot to stand. If the robot is sitting, it will stand up. If the robot is
    @@ -790,11 +816,8 @@ def stand_command(params=None, body_height=0.0, footprint_R_body=geometry.EulerZ
             return command
     
         @staticmethod
    -    @deprecated(
    -        reason='Mobility commands are now sent as a part of synchronized commands. '\
    -               'Use synchro_sit_command instead.',
    -        version='2.1.0',
    -        action="always")
    +    @deprecated(reason='Mobility commands are now sent as a part of synchronized commands. '
    +                'Use synchro_sit_command instead.', version='2.1.0', action="always")
         def sit_command(params=None):
             """
             Command the robot to sit.
    @@ -813,7 +836,6 @@ def sit_command(params=None):
             command = robot_command_pb2.RobotCommand(mobility_command=mobility_command)
             return command
     
    -
         #########################
         # Synchronized commands #
         #########################
    @@ -896,8 +918,9 @@ def synchro_se2_trajectory_command(goal_se2, frame_name, params=None, body_heigh
             return robot_command
     
         @staticmethod
    -    def synchro_trajectory_command_in_body_frame(goal_x_rt_body, goal_y_rt_body, goal_heading_rt_body,
    -                                                 frame_tree_snapshot, params=None, body_height=0.0,
    +    def synchro_trajectory_command_in_body_frame(goal_x_rt_body, goal_y_rt_body,
    +                                                 goal_heading_rt_body, frame_tree_snapshot,
    +                                                 params=None, body_height=0.0,
                                                      locomotion_hint=spot_command_pb2.HINT_AUTO):
             """Command robot to move to pose described relative to the robots body along a 2D plane. For example,
             a command to move forward 2 meters at the same heading will have goal_x_rt_body=2.0, goal_y_rt_body=0.0,
    @@ -929,8 +952,8 @@ def synchro_trajectory_command_in_body_frame(goal_x_rt_body, goal_y_rt_body, goa
             odom_tform_body = get_se2_a_tform_b(frame_tree_snapshot, ODOM_FRAME_NAME, BODY_FRAME_NAME)
             odom_tform_goto = odom_tform_body * goto_rt_body
             return RobotCommandBuilder.synchro_se2_trajectory_command(odom_tform_goto.to_proto(),
    -                                                                 ODOM_FRAME_NAME,
    -                                                                 params, body_height, locomotion_hint)
    +                                                                  ODOM_FRAME_NAME, params,
    +                                                                  body_height, locomotion_hint)
     
         @staticmethod
         def synchro_velocity_command(v_x, v_y, v_rot, params=None, body_height=0.0,
    @@ -1138,7 +1161,9 @@ def _arm_named_command(position, build_on_command=None):
             return robot_command
     
         @staticmethod
    -    def arm_gaze_command(x, y, z, frame_name, build_on_command=None):
    +    def arm_gaze_command(x, y, z, frame_name, build_on_command=None, frame2_tform_desired_hand=None,
    +                         frame2_name=None, max_linear_vel=None, max_angular_vel=None,
    +                         max_accel=None):
             """ Builds a Vec3Trajectory to tell the robot arm to gaze at a point in 3D space.
             Returns:
                 RobotCommand, which can be issued to the robot command service
    @@ -1147,10 +1172,26 @@ def arm_gaze_command(x, y, z, frame_name, build_on_command=None):
             point1 = trajectory_pb2.Vec3TrajectoryPoint(point=pos)
     
             traj = trajectory_pb2.Vec3Trajectory(points=[point1])
    -
             # Build the proto
             gaze_cmd = arm_command_pb2.GazeCommand.Request(target_trajectory_in_frame1=traj,
                                                            frame1_name=frame_name)
    +
    +        if frame2_tform_desired_hand is not None and frame2_name is not None:
    +            if type(frame2_tform_desired_hand) == SE3Pose:
    +                # Convert input argument from math_helpers class to protobuf message.
    +                frame2_tform_desired_hand = frame2_tform_desired_hand.to_proto()
    +
    +            desired_point = trajectory_pb2.SE3TrajectoryPoint(pose=frame2_tform_desired_hand)
    +            gaze_cmd.tool_trajectory_in_frame2.points.extend([desired_point])
    +            gaze_cmd.frame2_name = frame2_name
    +
    +        if max_linear_vel is not None:
    +            gaze_cmd.max_linear_velocity.value = max_linear_vel
    +        if max_angular_vel is not None:
    +            gaze_cmd.max_angular_velocity.value = max_angular_vel
    +        if max_accel is not None:
    +            gaze_cmd.maximum_acceleration.value = max_accel
    +
             arm_command = arm_command_pb2.ArmCommand.Request(arm_gaze_command=gaze_cmd)
             synchronized_command = synchronized_command_pb2.SynchronizedCommand.Request(
                 arm_command=arm_command)
    @@ -1187,7 +1228,8 @@ def arm_pose_command(x, y, z, qw, qx, qy, qz, frame_name, seconds=5, build_on_co
             return robot_command
     
         @staticmethod
    -    def arm_wrench_command(force_x, force_y, force_z, torque_x, torque_y, torque_z, frame_name, seconds=5, build_on_command=None):
    +    def arm_wrench_command(force_x, force_y, force_z, torque_x, torque_y, torque_z, frame_name,
    +                           seconds=5, build_on_command=None):
             """ Builds a command to tell robot arm to exhibit a wrench.
                 Wraps it in a SynchronizedCommand.
     
    @@ -1202,7 +1244,6 @@ def arm_wrench_command(force_x, force_y, force_z, torque_x, torque_y, torque_z,
                                                               time_since_reference=duration)
             trajectory = trajectory_pb2.WrenchTrajectory(points=[traj_point])
     
    -
             arm_cartesian_command = arm_command_pb2.ArmCartesianCommand.Request(
                 root_frame_name=frame_name, wrench_trajectory_in_task=trajectory,
                 x_axis=arm_command_pb2.ArmCartesianCommand.Request.AXIS_MODE_FORCE,
    @@ -1210,8 +1251,7 @@ def arm_wrench_command(force_x, force_y, force_z, torque_x, torque_y, torque_z,
                 z_axis=arm_command_pb2.ArmCartesianCommand.Request.AXIS_MODE_FORCE,
                 rx_axis=arm_command_pb2.ArmCartesianCommand.Request.AXIS_MODE_FORCE,
                 ry_axis=arm_command_pb2.ArmCartesianCommand.Request.AXIS_MODE_FORCE,
    -            rz_axis=arm_command_pb2.ArmCartesianCommand.Request.AXIS_MODE_FORCE
    -            )
    +            rz_axis=arm_command_pb2.ArmCartesianCommand.Request.AXIS_MODE_FORCE)
             arm_command = arm_command_pb2.ArmCommand.Request(
                 arm_cartesian_command=arm_cartesian_command)
             synchronized_command = synchronized_command_pb2.SynchronizedCommand.Request(
    @@ -1266,10 +1306,44 @@ def claw_gripper_open_angle_command(gripper_q, build_on_command=None):
                 return RobotCommandBuilder.build_synchro_command(build_on_command, command)
             return command
     
    +    @staticmethod
    +    def create_arm_joint_trajectory_point(sh0, sh1, el0, el1, wr0, wr1,
    +                                          time_since_reference_secs=None):
    +        joint_position = arm_command_pb2.ArmJointPosition(
    +            sh0=wrappers_pb2.DoubleValue(value=sh0), sh1=wrappers_pb2.DoubleValue(value=sh1),
    +            el0=wrappers_pb2.DoubleValue(value=el0), el1=wrappers_pb2.DoubleValue(value=el1),
    +            wr0=wrappers_pb2.DoubleValue(value=wr0), wr1=wrappers_pb2.DoubleValue(value=wr1))
    +        if time_since_reference_secs is not None:
    +            return arm_command_pb2.ArmJointTrajectoryPoint(
    +                position=joint_position,
    +                time_since_reference=seconds_to_duration(time_since_reference_secs))
    +        else:
    +            return arm_command_pb2.ArmJointTrajectoryPoint(position=joint_position)
    +
    +    @staticmethod
    +    def arm_joint_command(sh0, sh1, el0, el1, wr0, wr1, max_vel=None, max_accel=None,
    +                          build_on_command=None):
    +        traj_point1 = RobotCommandBuilder.create_arm_joint_trajectory_point(
    +            sh0, sh1, el0, el1, wr0, wr1)
    +        arm_joint_traj = arm_command_pb2.ArmJointTrajectory(points=[traj_point1])
    +
    +        if max_vel is not None:
    +            arm_joint_traj.maximum_velocity.value = max_vel
    +        if max_accel is not None:
    +            arm_joint_traj.maximum_acceleration.value = max_accel
    +
    +        joint_move_command = arm_command_pb2.ArmJointMoveCommand.Request(trajectory=arm_joint_traj)
    +        arm_command = arm_command_pb2.ArmCommand.Request(arm_joint_move_command=joint_move_command)
    +        sync_arm = synchronized_command_pb2.SynchronizedCommand.Request(arm_command=arm_command)
    +        arm_sync_robot_cmd = robot_command_pb2.RobotCommand(synchronized_command=sync_arm)
    +        if build_on_command:
    +            return RobotCommandBuilder.build_synchro_command(build_on_command, arm_sync_robot_cmd)
    +        return arm_sync_robot_cmd
     
         ########################
         # Spot mobility params #
         ########################
    +
         @staticmethod
         def mobility_params(body_height=0.0, footprint_R_body=geometry.EulerZXY(),
                             locomotion_hint=spot_command_pb2.HINT_AUTO, stair_hint=False,
    @@ -1304,6 +1378,7 @@ def mobility_params(body_height=0.0, footprint_R_body=geometry.EulerZXY(),
                                                    stair_hint=stair_hint,
                                                    external_force_params=external_force_params)
     
    +    @staticmethod
         def build_body_external_forces(
                 external_force_indicator=spot_command_pb2.BodyExternalForceParams.EXTERNAL_FORCE_NONE,
                 override_external_force_vec=None):
    @@ -1327,7 +1402,7 @@ def build_body_external_forces(
             """
             if external_force_indicator == spot_command_pb2.BodyExternalForceParams.EXTERNAL_FORCE_USE_OVERRIDE:
                 if override_external_force_vec is None:
    -                #Default the override forces to all zeros if none are specified
    +                # Default the override forces to all zeros if none are specified
                     override_external_force_vec = (0.0, 0.0, 0.0)
                 ext_forces = geometry_pb2.Vec3(x=override_external_force_vec[0],
                                                y=override_external_force_vec[1],
    @@ -1335,10 +1410,10 @@ def build_body_external_forces(
                 return spot_command_pb2.BodyExternalForceParams(
                     external_force_indicator=external_force_indicator, frame_name=BODY_FRAME_NAME,
                     external_force_override=ext_forces)
    -        elif (external_force_indicator ==
    -              spot_command_pb2.BodyExternalForceParams.EXTERNAL_FORCE_NONE or
    -              external_force_indicator ==
    -              spot_command_pb2.BodyExternalForceParams.EXTERNAL_FORCE_USE_ESTIMATE):
    +        elif (external_force_indicator
    +              == spot_command_pb2.BodyExternalForceParams.EXTERNAL_FORCE_NONE or
    +              external_force_indicator
    +              == spot_command_pb2.BodyExternalForceParams.EXTERNAL_FORCE_USE_ESTIMATE):
                 return spot_command_pb2.BodyExternalForceParams(
                     external_force_indicator=external_force_indicator)
             else:
    @@ -1440,8 +1515,12 @@ def blocking_stand(command_client, timeout_sec=10, update_frequency=1.0):
         raise CommandTimedOutError(
             "Took longer than {:.1f} seconds to assure the robot stood.".format(now - start_time))
     
    +
     def block_until_arm_arrives(command_client, cmd_id, timeout_sec=None):
    -    """Helper that blocks until the arm arrives at the end of its current trajectory.
    +    """Helper that blocks until the arm achieves a finishing state for the specific arm command.
    +
    +       This helper will block and check the feedback for ArmCartesianCommand, GazeCommand,
    +       ArmJointMoveCommand, and NamedArmPositionsCommand.
     
            Args:
             command_client: robot command client, used to request feedback
    @@ -1460,11 +1539,26 @@ def block_until_arm_arrives(command_client, cmd_id, timeout_sec=None):
     
         while timeout_sec is None or now < end_time:
             feedback_resp = command_client.robot_command_feedback(cmd_id)
    -
    -        if feedback_resp.feedback.synchronized_feedback.arm_command_feedback.arm_cartesian_feedback.status == arm_command_pb2.ArmCartesianCommand.Feedback.STATUS_TRAJECTORY_COMPLETE:
    -            return True
    -        elif feedback_resp.feedback.synchronized_feedback.arm_command_feedback.arm_cartesian_feedback.status == arm_command_pb2.ArmCartesianCommand.Feedback.STATUS_TRAJECTORY_STALLED or feedback_resp.feedback.synchronized_feedback.arm_command_feedback.arm_cartesian_feedback.status == arm_command_pb2.ArmCartesianCommand.Feedback.STATUS_TRAJECTORY_CANCELLED:
    -            return False
    +        arm_feedback = feedback_resp.feedback.synchronized_feedback.arm_command_feedback
    +
    +        if arm_feedback.HasField("arm_cartesian_feedback"):
    +            if arm_feedback.arm_cartesian_feedback.status == arm_command_pb2.ArmCartesianCommand.Feedback.STATUS_TRAJECTORY_COMPLETE:
    +                return True
    +            elif arm_feedback.arm_cartesian_feedback.status == arm_command_pb2.ArmCartesianCommand.Feedback.STATUS_TRAJECTORY_STALLED or feedback_resp.feedback.synchronized_feedback.arm_command_feedback.arm_cartesian_feedback.status == arm_command_pb2.ArmCartesianCommand.Feedback.STATUS_TRAJECTORY_CANCELLED:
    +                return False
    +        elif arm_feedback.HasField("arm_gaze_feedback"):
    +            if arm_feedback.arm_gaze_feedback.status == arm_command_pb2.GazeCommand.Feedback.STATUS_TRAJECTORY_COMPLETE:
    +                return True
    +            elif arm_feedback.arm_gaze_feedback.status == arm_command_pb2.GazeCommand.Feedback.STATUS_TOOL_TRAJECTORY_STALLED:
    +                return False
    +        elif arm_feedback.HasField("arm_joint_move_feedback"):
    +            if arm_feedback.arm_joint_move_feedback.status == arm_command_pb2.ArmJointMoveCommand.Feedback.STATUS_COMPLETE:
    +                return True
    +        elif arm_feedback.HasField("named_arm_position_feedback"):
    +            if arm_feedback.named_arm_position_feedback.status == arm_command_pb2.NamedArmPositionsCommand.Feedback.STATUS_COMPLETE:
    +                return True
    +            elif arm_feedback.named_arm_position_feedback.status == arm_command_pb2.NamedArmPositionsCommand.Feedback.STATUS_STALLED_HOLDING_ITEM:
    +                return False
     
             time.sleep(0.1)
             now = time.time()
    diff --git a/python/bosdyn-client/src/bosdyn/client/robot_state.py b/python/bosdyn-client/src/bosdyn/client/robot_state.py
    index fe045b146..b87c5bc47 100644
    --- a/python/bosdyn-client/src/bosdyn/client/robot_state.py
    +++ b/python/bosdyn-client/src/bosdyn/client/robot_state.py
    @@ -146,6 +146,7 @@ def _get_robot_hardware_configuration_value(response):
     def _get_robot_link_model_value(response):
         return response.link_model
     
    +
     def has_arm(state_client, timeout=None):
         """Check if the robot has an arm attached.
     
    diff --git a/python/bosdyn-client/src/bosdyn/client/sdk.py b/python/bosdyn-client/src/bosdyn/client/sdk.py
    index 94c01a333..d1cd695ff 100644
    --- a/python/bosdyn-client/src/bosdyn/client/sdk.py
    +++ b/python/bosdyn-client/src/bosdyn/client/sdk.py
    @@ -19,6 +19,7 @@
     
     from .arm_surface_contact import ArmSurfaceContactClient
     from .auth import AuthClient
    +from .auto_return import AutoReturnClient
     from .channel import DEFAULT_MAX_MESSAGE_LENGTH
     from .data_acquisition import DataAcquisitionClient
     from .data_acquisition_store import DataAcquisitionStoreClient
    @@ -33,11 +34,13 @@
     from .exceptions import Error
     from .graph_nav import GraphNavClient
     from .image import ImageClient
    +from .ir_enable_disable import IREnableDisableServiceClient
     from .lease import LeaseClient
     from .license import LicenseClient
     from .log_annotation import LogAnnotationClient
     from .local_grid import LocalGridClient
     from .manipulation_api_client import ManipulationApiClient
    +from .map_processing import MapProcessingServiceClient
     from .network_compute_bridge_client import NetworkComputeBridgeClient
     from .payload import PayloadClient
     from .payload_registration import PayloadRegistrationClient
    @@ -53,6 +56,7 @@
     from .time_sync import TimeSyncClient
     from .world_object import WorldObjectClient
     
    +
     class SdkError(Error):
         """General class of errors to handle non-response non-rpc errors."""
     
    @@ -94,6 +98,7 @@ def generate_client_name(prefix=''):
     _DEFAULT_SERVICE_CLIENTS = [
         ArmSurfaceContactClient,
         AuthClient,
    +    AutoReturnClient,
         DataAcquisitionClient,
         DataAcquisitionStoreClient,
         DataBufferClient,
    @@ -107,11 +112,13 @@ def generate_client_name(prefix=''):
         GraphNavClient,
         GraphNavRecordingServiceClient,
         ImageClient,
    +    IREnableDisableServiceClient,
         LeaseClient,
         LicenseClient,
         LogAnnotationClient,
         LocalGridClient,
         ManipulationApiClient,
    +    MapProcessingServiceClient,
         NetworkComputeBridgeClient,
         PayloadClient,
         PayloadRegistrationClient,
    @@ -265,11 +272,20 @@ def load_robot_cert(self, resource_path_glob=None):
                     with open(cert_path, 'rb') as cert_file:
                         self.cert += cert_file.read()
     
    +    def clear_robots(self):
    +        """Remove all cached Robot instances.
    +        Subsequent calls to create_robot() will return newly created Robots.
    +        Existing robot instances will continue to work, but their time sync and token refresh
    +        threads will be stopped.
    +        """
    +        for robot in self.robots.values():
    +            robot._shutdown()
    +        self.robots = {}
    +
         @staticmethod
         @deprecated(
             reason='App tokens are no longer in use. Authorization is now handled via licenses.',
    -        version='2.0.1',
    -        action="always")
    +        version='2.0.1', action="always")
         def load_app_token(*_):
             """Do nothing, this method is kept only to maintain backwards compatibility."""
             return
    diff --git a/python/bosdyn-client/src/bosdyn/client/server_util.py b/python/bosdyn-client/src/bosdyn/client/server_util.py
    index 91b6a7b48..f503aa044 100644
    --- a/python/bosdyn-client/src/bosdyn/client/server_util.py
    +++ b/python/bosdyn-client/src/bosdyn/client/server_util.py
    @@ -4,13 +4,28 @@
     # is subject to the terms and conditions of the Boston Dynamics Software
     # Development Kit License (20191101-BDSDK-SL).
     
    +"""Helper functions and classes for creating and running a gRPC service."""
    +
    +from concurrent import futures
    +import copy
    +import grpc
     import logging
    +import signal
    +import time
    +
     from bosdyn.api import header_pb2
    -import bosdyn.util
    +from bosdyn.api import data_acquisition_store_pb2
    +from bosdyn.api import data_buffer_pb2
    +from bosdyn.api import image_pb2
    +from bosdyn.api import local_grid_pb2
    +from bosdyn.api import log_annotation_pb2
    +from bosdyn.client.channel import generate_channel_options
     
    +import bosdyn.util
     
     _LOGGER = logging.getLogger(__name__)
     
    +
     class ResponseContext(object):
         """Helper to log gRPC request and response message to the data buffer for a service.
     
    @@ -24,19 +39,25 @@ class ResponseContext(object):
             request (protobuf): any gRPC request message with a bosdyn.api.RequestHeader proto.
             rpc_logger (DataBufferClient): Optional data buffer client to log the messages; if not
                 provided, only the headers will be mutated and nothing will be logged.
    +        channel_prefix (string): the prefix you want this req / resp pair logged under.
         """
     
    -    def __init__(self, response, request, rpc_logger=None):
    +    def __init__(self, response, request, rpc_logger=None, channel_prefix=None):
             self.response = response
             self.response.header.request_header.CopyFrom(request.header)
             self.request = request
             self.rpc_logger = rpc_logger
    +        self.channel_prefix = channel_prefix
     
         def __enter__(self):
             """Adds a start timestamp to the response header and logs the request RPC."""
             self.response.header.request_received_timestamp.CopyFrom(bosdyn.util.now_timestamp())
             if self.rpc_logger:
    -            self.rpc_logger.add_protobuf_async(self.request)
    +            if self.channel_prefix is None:
    +                channel = None
    +            else:
    +                channel = self.channel_prefix + "/" + self.request.DESCRIPTOR.full_name
    +            self.rpc_logger.add_protobuf_async(self.request, channel)
             return self.response
     
         def __exit__(self, exc_type, exc_val, exc_tb):
    @@ -49,5 +70,161 @@ def __exit__(self, exc_type, exc_val, exc_tb):
                 self.response.header.error.code = self.response.header.error.CODE_INTERNAL_SERVER_ERROR
                 self.response.header.error.message = "[%s] %s" % (exc_type.__name__, exc_val)
             if self.rpc_logger:
    -            self.rpc_logger.add_protobuf_async(self.response)
    +            if self.channel_prefix is None:
    +                channel = None
    +            else:
    +                channel = self.channel_prefix + "/" + self.response.DESCRIPTOR.full_name
    +            self.rpc_logger.add_protobuf_async(self.response, channel)
    +
    +
    +class GrpcServiceRunner(object):
    +    """A runner to start a gRPC server on a background thread and allow easy cleanup.
    +
    +    Args:
    +        service_servicer (custom servicer class derived from ServiceServicer): Servicer that
    +            defines server behavior.
    +        add_servicer_to_server_fn (function): Function generated by gRPC compilation that
    +            attaches the servicer to the gRPC server.
    +        port (int): The port number the service can be accessed through on the host system.
    +            Defaults to 0, which will assign an ephemeral port.
    +        max_send_message_length (int): Max message length (bytes) allowed for messages sent.
    +        max_receive_message_length (int): Max message length (bytes) allowed for messages received.
    +        timeout_secs (int): Number of seconds to wait for a clean server shutdown.
    +        force_sigint_capture (bool): Re-assign the SIGINT handler to default in order to prevent
    +            other scripts from blocking a clean exit. Defaults to True.
    +        logger (logging.Logger): Logger to log with.
    +    """
    +
    +    def __init__(self, service_servicer, add_servicer_to_server_fn, port=0, max_workers=4,
    +                 max_send_message_length=None, max_receive_message_length=None, timeout_secs=3,
    +                 force_sigint_capture=True, logger=None):
    +        self.logger = logger or _LOGGER
    +        self.timeout_secs = timeout_secs
    +        self.force_sigint_capture = force_sigint_capture
    +
    +        # Use the name of the service_servicer class for print messages.
    +        self.server_type_name = type(service_servicer).__name__
    +
    +        self.server = grpc.server(
    +            futures.ThreadPoolExecutor(max_workers=max_workers),
    +            options=generate_channel_options(max_send_message_length, max_receive_message_length))
    +        add_servicer_to_server_fn(service_servicer, self.server)
    +        self.port = self.server.add_insecure_port('[::]:{}'.format(port))
    +        self.server.start()
    +        self.logger.info('Started the {} server.'.format(self.server_type_name))
    +
    +    def __enter__(self):
    +        return self
    +
    +    def __exit__(self, exc_type, exc_val, exc_tb):
    +        self.stop()
    +
    +    def stop(self):
    +        """Blocks until the gRPC server shutsdown."""
    +        self.logger.info("Shutting down the {} server.".format(self.server_type_name))
    +        shutdown_complete = self.server.stop(None)
    +        shutdown_complete.wait(self.timeout_secs)
    +
    +    def run_until_interrupt(self):
    +        """Spin the thread until a SIGINT is received and then shut down cleanly."""
    +        if self.force_sigint_capture:
    +            # Ensure that KeyboardInterrupt is raised on a SIGINT.
    +            signal.signal(signal.SIGINT, signal.default_int_handler)
    +
    +        # Monitor for SIGINT and shut down cleanly.
    +        try:
    +            while True:
    +                time.sleep(1)
    +        except KeyboardInterrupt:
    +            pass
    +        self.stop()
    +
    +
    +def populate_response_header(response, request, error_code=header_pb2.CommonError.CODE_OK,
    +                             error_msg=None):
    +    """Sets the ResponseHeader header in the response.
    +    Args:
    +        response (bosdyn.api Response message): The GRPC response message to be populated.
    +        request (bosdyn.api Request message): The header from the request is added to the response.
    +        error_code (header_pb2.CommonError): The status for the RPC response.
    +        error_msg (str): An optional error message describing a bad header status failure.
    +    Returns:
    +        Mutates the response message's header to be fully populated.
    +    """
    +    header = header_pb2.ResponseHeader()
    +    header.request_received_timestamp.CopyFrom(bosdyn.util.now_timestamp())
    +    header.request_header.CopyFrom(request.header)
    +    header.error.code = error_code
    +    if error_msg:
    +        header.error.message = error_msg
    +    copied_request = copy.copy(request)
    +    strip_large_bytes_fields(copied_request)
    +    header.request.Pack(copied_request)
    +    response.header.CopyFrom(header)
    +
    +
    +def strip_large_bytes_fields(proto_message):
    +    """Removes any large bytes fields from a protobuf message depending on the proto type."""
    +    message_type = type(proto_message)
    +    allowlist_map = get_bytes_field_allowlist()
    +    if message_type in allowlist_map:
    +        allowlist_map[message_type](proto_message)
    +
    +
    +def get_bytes_field_allowlist():
    +    """Creates set of protos which will have bytes fields removed."""
    +    allowlist_map = {
    +        image_pb2.GetImageResponse: strip_get_image_response,
    +        local_grid_pb2.GetLocalGridsResponse: strip_local_grid_responses,
    +        data_acquisition_store_pb2.StoreDataRequest: strip_store_data_request,
    +        data_acquisition_store_pb2.StoreImageRequest: strip_store_image_request,
    +        data_buffer_pb2.RecordSignalTicksRequest: strip_record_signal_tick,
    +        data_buffer_pb2.RecordDataBlobsRequest: strip_record_data_blob,
    +        log_annotation_pb2.AddLogAnnotationRequest: strip_log_annotation
    +    }
    +    return allowlist_map
    +
    +
    +def strip_image_response(proto_message):
    +    """Removes bytes from the image_pb2.ImageResponse proto."""
    +    proto_message.shot.image.ClearField("data")
    +
    +
    +def strip_get_image_response(proto_message):
    +    """Removes bytes from the image_pb2.GetImageResponse proto."""
    +    for img_resp in proto_message.image_responses:
    +        strip_image_response(img_resp)
    +
    +
    +def strip_local_grid_responses(proto_message):
    +    """Removes bytes from the local_grid_pb2.GetLocalGridsResponse proto."""
    +    for grid_resp in proto_message.local_grid_responses:
    +        grid_resp.local_grid.ClearField("data")
    +
    +
    +def strip_store_image_request(proto_message):
    +    """Removes bytes from the data_acquisition_store_pb2.StoreImageRequest proto."""
    +    proto_message.image.image.ClearField("data")
    +
    +
    +def strip_store_data_request(proto_message):
    +    """Removes bytes from the data_acquisition_store_pb2.StoreDataRequest proto."""
    +    proto_message.ClearField("data")
    +
    +
    +def strip_record_signal_tick(proto_message):
    +    """Removes bytes from the data_buffer_pb2.RecordSignalTicksRequest proto."""
    +    for tick_data in proto_message.tick_data:
    +        tick_data.ClearField("data")
    +
    +
    +def strip_record_data_blob(proto_message):
    +    """Removes bytes from the data_buffer_pb2.RecordDataBlobsRequest proto."""
    +    for blob in proto_message.blob_data:
    +        blob.ClearField("data")
    +
     
    +def strip_log_annotation(proto_message):
    +    """Removes bytes from the log_annotation_pb2.AddLogAnnotationRequest proto."""
    +    for blob in proto_message.annotations.blob_data:
    +        blob.ClearField("data")
    diff --git a/python/bosdyn-client/src/bosdyn/client/spot_cam/__init__.py b/python/bosdyn-client/src/bosdyn/client/spot_cam/__init__.py
    index 40c8f3d44..19a5cb2a3 100644
    --- a/python/bosdyn-client/src/bosdyn/client/spot_cam/__init__.py
    +++ b/python/bosdyn-client/src/bosdyn/client/spot_cam/__init__.py
    @@ -28,6 +28,7 @@
         version.VersionClient
     ]
     
    +
     def register_all_service_clients(sdk):
         for client in CLIENTS:
             sdk.register_service_client(client)
    diff --git a/python/bosdyn-client/src/bosdyn/client/spot_cam/audio.py b/python/bosdyn-client/src/bosdyn/client/spot_cam/audio.py
    index 95ba12074..d2a9b451e 100644
    --- a/python/bosdyn-client/src/bosdyn/client/spot_cam/audio.py
    +++ b/python/bosdyn-client/src/bosdyn/client/spot_cam/audio.py
    @@ -116,6 +116,76 @@ def yield_requests(data):
             return self.call(self._stub.LoadSound, yield_requests(data), self._load_sound_from_response,
                              self._audio_error_from_response, **kwargs)
     
    +    #
    +    # RPCs for Spot CAM+IR Only
    +    #
    +
    +    def set_audio_capture_channel(self, channel, **kwargs):
    +        """Set the audio capture channel
    +        
    +        Args:
    +            channel (audio_pb2.AudioCaptureChannel): Microphone to use
    +        """
    +        request = audio_pb2.SetAudioCaptureChannelRequest(channel=channel)
    +        return self.call(self._stub.SetAudioCaptureChannel, request, None,
    +                         self._audio_error_from_response, **kwargs)
    +
    +    def set_audio_capture_channel_async(self, channel, **kwargs):
    +        """Async version of set_audio_capture_channel()"""
    +        request = audio_pb2.SetAudioCaptureChannelRequest(channel=channel)
    +        return self.call_async(self._stub.SetAudioCaptureChannel, request, None,
    +                               self._audio_error_from_response, **kwargs)
    +
    +    def get_audio_capture_channel(self, **kwargs):
    +        """Retrieve the audio capture channel (microphone)
    +        """
    +        request = audio_pb2.GetAudioCaptureChannelRequest()
    +        return self.call(self._stub.GetAudioCaptureChannel, request,
    +                         self._get_audio_capture_channel_from_response,
    +                         self._audio_error_from_response, **kwargs)
    +
    +    def get_audio_capture_channel_async(self, **kwargs):
    +        """Async version of get_audio_capture_channel()"""
    +        request = audio_pb2.GetAudioCaptureChannelRequest()
    +        return self.call_async(self._stub.GetAudioCaptureChannel, request,
    +                               self._get_audio_capture_channel_from_response,
    +                               self._audio_error_from_response, **kwargs)
    +
    +    def set_audio_capture_gain(self, channel, gain, **kwargs):
    +        """Set the audio capture gain
    +        
    +        Args:
    +            channel (audio_pb2.AudioCaptureChannel): Microphone to set gain for
    +            gain (Double): Microphone gain, 0.0 to 1.0
    +        """
    +        request = audio_pb2.SetAudioCaptureGainRequest(channel=channel, gain=gain)
    +        return self.call(self._stub.SetAudioCaptureGain, request, None,
    +                         self._audio_error_from_response, **kwargs)
    +
    +    def set_audio_capture_gain_async(self, channel, gain, **kwargs):
    +        """Async version of set_audio_capture_gain()"""
    +        request = audio_pb2.SetAudioCaptureGainRequest(channel=channel, gain=gain)
    +        return self.call_async(self._stub.SetAudioCaptureGain, request, None,
    +                               self._audio_error_from_response, **kwargs)
    +
    +    def get_audio_capture_gain(self, channel, **kwargs):
    +        """Retrieve the audio capture gain (microphone volume)
    +        
    +        Args:
    +            channel (audio_pb2.AudioCaptureChannel): Microphone to get gain for
    +        """
    +        request = audio_pb2.GetAudioCaptureGainRequest(channel=channel)
    +        return self.call(self._stub.GetAudioCaptureGain, request,
    +                         self._get_audio_capture_gain_from_response,
    +                         self._audio_error_from_response, **kwargs)
    +
    +    def get_audio_capture_gain_async(self, channel, **kwargs):
    +        """Async version of get_audio_capture_gain()"""
    +        request = audio_pb2.GetAudioCaptureGainRequest(channel=channel)
    +        return self.call_async(self._stub.GetAudioCaptureGain, request,
    +                               self._get_audio_capture_gain_from_response,
    +                               self._audio_error_from_response, **kwargs)
    +
         @staticmethod
         def _list_sounds_from_response(response):
             return response.sounds
    @@ -140,6 +210,14 @@ def _delete_sound_from_response(response):
         def _load_sound_from_response(response):
             pass
     
    +    @staticmethod
    +    def _get_audio_capture_channel_from_response(response):
    +        return response.channel
    +
    +    @staticmethod
    +    def _get_audio_capture_gain_from_response(response):
    +        return response.gain
    +
         @staticmethod
         @handle_common_header_errors
         def _audio_error_from_response(response):  # pylint: disable=unused-argument
    diff --git a/python/bosdyn-client/src/bosdyn/client/spot_cam/compositor.py b/python/bosdyn-client/src/bosdyn/client/spot_cam/compositor.py
    index a07cfaaed..84f28bfd2 100644
    --- a/python/bosdyn-client/src/bosdyn/client/spot_cam/compositor.py
    +++ b/python/bosdyn-client/src/bosdyn/client/spot_cam/compositor.py
    @@ -12,6 +12,7 @@
     
     from google.protobuf.wrappers_pb2 import BoolValue
     
    +
     class CompositorClient(BaseClient):
         """A client calling Spot CAM Compositor services.
         """
    diff --git a/python/bosdyn-client/src/bosdyn/client/spot_cam/health.py b/python/bosdyn-client/src/bosdyn/client/spot_cam/health.py
    index 76a7a1278..b7d82f25a 100644
    --- a/python/bosdyn-client/src/bosdyn/client/spot_cam/health.py
    +++ b/python/bosdyn-client/src/bosdyn/client/spot_cam/health.py
    @@ -14,6 +14,7 @@
     from bosdyn.api.spot_cam import service_pb2_grpc
     from bosdyn.api.spot_cam import health_pb2
     
    +
     class HealthClient(BaseClient):
         """A client calling Spot CAM Health service.
         """
    @@ -32,7 +33,8 @@ def clear_bit_events(self, **kwargs):
         def clear_bit_events_async(self, **kwargs):
             """Async version of clear_bit_events()."""
             request = health_pb2.ClearBITEventsRequest()
    -        return self.call_async(self._stub.ClearBITEvents, request, self._clear_bit_events_from_response,
    +        return self.call_async(self._stub.ClearBITEvents, request,
    +                               self._clear_bit_events_from_response,
                                    self._health_error_from_response, **kwargs)
     
         def get_bit_status(self, **kwargs):
    @@ -56,7 +58,8 @@ def get_temperature(self, **kwargs):
         def get_temperature_async(self, **kwargs):
             """Async version of get_temperature()."""
             request = health_pb2.GetTemperatureRequest()
    -        return self.call_async(self._stub.GetTemperature, request, self._get_temperature_from_response,
    +        return self.call_async(self._stub.GetTemperature, request,
    +                               self._get_temperature_from_response,
                                    self._health_error_from_response, **kwargs)
     
         @staticmethod
    diff --git a/python/bosdyn-client/src/bosdyn/client/spot_cam/media_log.py b/python/bosdyn-client/src/bosdyn/client/spot_cam/media_log.py
    index 6735770be..4d6283b59 100644
    --- a/python/bosdyn-client/src/bosdyn/client/spot_cam/media_log.py
    +++ b/python/bosdyn-client/src/bosdyn/client/spot_cam/media_log.py
    @@ -10,6 +10,8 @@
     
     _LOGGER = logging.getLogger(__name__)
     
    +from deprecated import deprecated
    +
     from bosdyn.client.common import (BaseClient, common_header_errors, handle_common_header_errors)
     from bosdyn.api.spot_cam import service_pb2_grpc
     from bosdyn.api.spot_cam import logging_pb2
    @@ -39,7 +41,8 @@ def delete_async(self, logpoint, **kwargs):
             return self.call_async(self._stub.Delete, request, self._delete_from_response,
                                    self._media_log_error_from_response, **kwargs)
     
    -    def enable_debug(self, temp=False, humidity=False, bit=False, shock=True, system_stats=False, **kwargs):
    +    def enable_debug(self, temp=False, humidity=False, bit=False, shock=True, system_stats=False,
    +                     **kwargs):
             """Start periodic logging of health data to the database, queryable via Health service.
     
             Args:
    @@ -48,21 +51,18 @@ def enable_debug(self, temp=False, humidity=False, bit=False, shock=True, system
               bit: Enable logging of BIT events coming from the Health service.
               shock: Enable logging of Shock data.
               system_stats: Enable logging of cpu, gpu, memory, and network utilization."""
    -        request = logging_pb2.DebugRequest(enable_temperature=temp,
    -                                           enable_humidity=humidity,
    -                                           enable_BIT=bit,
    -                                           enable_shock=shock,
    +        request = logging_pb2.DebugRequest(enable_temperature=temp, enable_humidity=humidity,
    +                                           enable_BIT=bit, enable_shock=shock,
                                                enable_system_stat=system_stats)
             return self.call(self._stub.EnableDebug, request, self._enable_debug_from_response,
                              self._media_log_error_from_response, **kwargs)
     
    -    def enable_debug_async(self, temp=False, humidity=False, bit=False, shock=True, system_stats=False, **kwargs):
    +    def enable_debug_async(self, temp=False, humidity=False, bit=False, shock=True,
    +                           system_stats=False, **kwargs):
             """Async version of enable_debug()"""
    -        request = logging_pb2.DebugRequest(enable_temperature=temp,
    -                                                 enable_humidity=humidity,
    -                                                 enable_BIT=bit,
    -                                                 enable_shock=shock,
    -                                                 enable_system_stat=system_stats)
    +        request = logging_pb2.DebugRequest(enable_temperature=temp, enable_humidity=humidity,
    +                                           enable_BIT=bit, enable_shock=shock,
    +                                           enable_system_stat=system_stats)
             return self.call_async(self._stub.EnableDebug, request, self._enable_debug_from_response,
                                    self._media_log_error_from_response, **kwargs)
     
    @@ -119,16 +119,21 @@ def retrieve_raw_data(self, logpoint, **kwargs):
             return self.call(self._stub.RetrieveRawData, request, self._retrieve_from_response,
                              self._media_log_error_from_response, **kwargs)
     
    +    @deprecated(reason='Spot CAM encryption has been removed as a result of the switch to NTFS.',
    +                version='3.0.0', action="always")
         def set_passphrase(self, passphrase, **kwargs):
             """Set password for Spot CAM filesystem."""
             request = logging_pb2.SetPassphraseRequest(passphrase=passphrase)
             return self.call(self._stub.SetPassphrase, request, self._set_passphrase_from_response,
                              self._media_log_error_from_response, **kwargs)
     
    +    @deprecated(reason='Spot CAM encryption has been removed as a result of the switch to NTFS.',
    +                version='3.0.0', action="always")
         def set_passphrase_async(self, passphrase, **kwargs):
             """Async version of set_passphrase()"""
             request = logging_pb2.SetPassphraseRequest(passphrase=passphrase)
    -        return self.call_async(self._stub.SetPassphrase, request, self._set_passphrase_from_response,
    +        return self.call_async(self._stub.SetPassphrase, request,
    +                               self._set_passphrase_from_response,
                                    self._media_log_error_from_response, **kwargs)
     
         def store(self, camera, record_type, tag=None, **kwargs):
    @@ -201,8 +206,8 @@ def _retrieve_from_response(responses):
                     logpoint = response.logpoint
                 chunk = response.data
                 total += len(chunk.data)
    -            _LOGGER.debug('Retrieved {} bytes ({}/{})'.format(
    -                len(chunk.data), total, chunk.total_size))
    +            _LOGGER.debug('Retrieved {} bytes ({}/{})'.format(len(chunk.data), total,
    +                                                              chunk.total_size))
                 local_chunks.append(chunk)
             return logpoint, b''.join(chunk.data for chunk in local_chunks)
     
    diff --git a/python/bosdyn-client/src/bosdyn/client/spot_cam/network.py b/python/bosdyn-client/src/bosdyn/client/spot_cam/network.py
    index 6998c7c04..6963b5fb5 100644
    --- a/python/bosdyn-client/src/bosdyn/client/spot_cam/network.py
    +++ b/python/bosdyn-client/src/bosdyn/client/spot_cam/network.py
    @@ -7,12 +7,13 @@
     """For clients to the Spot CAM Network service."""
     
     import socket
    -import struct 
    +import struct
     
     from bosdyn.client.common import (BaseClient, handle_common_header_errors)
     from bosdyn.api.spot_cam import service_pb2_grpc
     from bosdyn.api.spot_cam import network_pb2
     
    +
     class NetworkClient(BaseClient):
         """A client calling Spot CAM Network services such as ICE Candidates, SSL certs / Keys etc.
     
    diff --git a/python/bosdyn-client/src/bosdyn/client/spot_cam/power.py b/python/bosdyn-client/src/bosdyn/client/spot_cam/power.py
    index a29980ec4..0bb144432 100644
    --- a/python/bosdyn-client/src/bosdyn/client/spot_cam/power.py
    +++ b/python/bosdyn-client/src/bosdyn/client/spot_cam/power.py
    @@ -16,6 +16,7 @@
     
     from google.protobuf.wrappers_pb2 import BoolValue
     
    +
     class PowerClient(BaseClient):
         """A client calling Spot CAM Power service.
         """
    @@ -34,7 +35,8 @@ def get_power_status(self, **kwargs):
         def get_power_status_async(self, **kwargs):
             """Async version of get_power_status()"""
             request = power_pb2.GetPowerStatusRequest()
    -        return self.call_async(self._stub.GetPowerStatus, request, self._get_power_status_from_response,
    +        return self.call_async(self._stub.GetPowerStatus, request,
    +                               self._get_power_status_from_response,
                                    self._power_error_from_response, **kwargs)
     
         def set_power_status(self, ptz=None, aux1=None, aux2=None, external_mic=None, **kwargs):
    @@ -52,7 +54,8 @@ def set_power_status_async(self, ptz=None, aux1=None, aux2=None, external_mic=No
             """Async version of set_power_status()"""
             request = self._build_SetPowerStatusRequest(ptz, aux1, aux2, external_mic)
     
    -        return self.call_async(self._stub.SetPowerStatus, request, self._set_power_status_from_response,
    +        return self.call_async(self._stub.SetPowerStatus, request,
    +                               self._set_power_status_from_response,
                                    self._power_error_from_response, **kwargs)
     
         def cycle_power(self, ptz=None, aux1=None, aux2=None, external_mic=None, **kwargs):
    diff --git a/python/bosdyn-client/src/bosdyn/client/spot_cam/ptz.py b/python/bosdyn-client/src/bosdyn/client/spot_cam/ptz.py
    index 09750fcd8..19333ebf2 100644
    --- a/python/bosdyn-client/src/bosdyn/client/spot_cam/ptz.py
    +++ b/python/bosdyn-client/src/bosdyn/client/spot_cam/ptz.py
    @@ -144,6 +144,15 @@ def _set_ptz_velocity_from_response(response):
         def _initialize_lens_from_response(response):
             return response
     
    +    # Focus methods
    +    @staticmethod
    +    def _get_ptz_focus_from_response(response):
    +        return response.ptz_focus
    +
    +    @staticmethod
    +    def _set_ptz_focus_from_response(response):
    +        return response.ptz_focus
    +
     def shift_pan_angle(pan):
         """Shift the pan angle (degrees) so that it is in the [0,360] range."""
         return recenter_angle(pan, 0, 360)
    \ No newline at end of file
    diff --git a/python/bosdyn-client/src/bosdyn/client/spot_cam/streamquality.py b/python/bosdyn-client/src/bosdyn/client/spot_cam/streamquality.py
    index 6df8f9525..eff52c7fa 100644
    --- a/python/bosdyn-client/src/bosdyn/client/spot_cam/streamquality.py
    +++ b/python/bosdyn-client/src/bosdyn/client/spot_cam/streamquality.py
    @@ -14,6 +14,7 @@
     from bosdyn.api.spot_cam import service_pb2_grpc
     from bosdyn.api.spot_cam import streamquality_pb2
     
    +
     class StreamQualityClient(BaseClient):
         """A client calling Spot CAM StreamQuality service.
         """
    @@ -23,16 +24,20 @@ class StreamQualityClient(BaseClient):
         def __init__(self):
             super(StreamQualityClient, self).__init__(service_pb2_grpc.StreamQualityServiceStub)
     
    -    def set_stream_params(self, target_bitrate=None, refresh_interval=None, idr_interval=None, awb_mode=None, **kwargs):
    +    def set_stream_params(self, target_bitrate=None, refresh_interval=None, idr_interval=None,
    +                          awb_mode=None, **kwargs):
             """Change image compression and postprocessing."""
    -        request = self._build_SetStreamParamsRequest(target_bitrate, refresh_interval, idr_interval, awb_mode)
    +        request = self._build_SetStreamParamsRequest(target_bitrate, refresh_interval, idr_interval,
    +                                                     awb_mode)
     
             return self.call(self._stub.SetStreamParams, request, self._params_from_response,
                              self._streamquality_error_from_response, **kwargs)
     
    -    def set_stream_params_async(self, target_bitrate=None, refresh_interval=None, idr_interval=None, awb_mode=None, **kwargs):
    +    def set_stream_params_async(self, target_bitrate=None, refresh_interval=None, idr_interval=None,
    +                                awb_mode=None, **kwargs):
             """Async version of set_stream_params()."""
    -        request = self._build_SetStreamParamsRequest(target_bitrate, refresh_interval, idr_interval, awb_mode)
    +        request = self._build_SetStreamParamsRequest(target_bitrate, refresh_interval, idr_interval,
    +                                                     awb_mode)
     
             return self.call_async(self._stub.SetStreamParams, request, self._params_from_response,
                                    self._streamquality_error_from_response, **kwargs)
    @@ -49,6 +54,18 @@ def get_stream_params_async(self, **kwargs):
             return self.call_async(self._stub.GetStreamParams, request, self._params_from_response,
                                    self._streamquality_error_from_response, **kwargs)
     
    +    def enable_congestion_control(self, enable=True, **kwargs):
    +        """Enable congestion control."""
    +        request = streamquality_pb2.EnableCongestionControlRequest(enable_congestion_control=enable)
    +        return self.call(self._stub.EnableCongestionControl, request, None,
    +                         self._streamquality_error_from_response, **kwargs)
    +
    +    def enable_congestion_control_async(self, enable=True, **kwargs):
    +        """Async version of enable_congestion_control()."""
    +        request = streamquality_pb2.EnableCongestionControlRequest(enable_congestion_control=enable)
    +        return self.call_async(self._stub.EnableCongestionControl, request, None,
    +                               self._streamquality_error_from_response, **kwargs)
    +
         @staticmethod
         def _build_SetStreamParamsRequest(target_bitrate, refresh_interval, idr_interval, awb_mode):
             request = streamquality_pb2.SetStreamParamsRequest()
    diff --git a/python/bosdyn-client/src/bosdyn/client/time_sync.py b/python/bosdyn-client/src/bosdyn/client/time_sync.py
    index e64012076..8a840af65 100644
    --- a/python/bosdyn-client/src/bosdyn/client/time_sync.py
    +++ b/python/bosdyn-client/src/bosdyn/client/time_sync.py
    @@ -19,6 +19,7 @@
     from bosdyn.api.time_range_pb2 import TimeRange
     from bosdyn.util import (RobotTimeConverter, now_nsec, parse_timespan, nsec_to_timestamp,
                              set_timestamp_from_nsec, timestamp_to_nsec)
    +from google.protobuf import duration_pb2
     
     from .common import BaseClient, common_header_errors
     from .exceptions import Error
    @@ -135,8 +136,8 @@ def _datetime_to_nsec(date_time):
                 return date_time.timestamp() * 1e9
             return None
     
    -    return robot_time_range_from_nanoseconds(
    -        _datetime_to_nsec(start_datetime), _datetime_to_nsec(end_datetime), time_sync_endpoint)
    +    return robot_time_range_from_nanoseconds(_datetime_to_nsec(start_datetime),
    +                                             _datetime_to_nsec(end_datetime), time_sync_endpoint)
     
     
     def timespec_to_robot_timespan(timespan_spec, time_sync_endpoint=None):
    @@ -158,6 +159,8 @@ def timespec_to_robot_timespan(timespan_spec, time_sync_endpoint=None):
         return robot_time_range_from_datetimes(start_datetime, end_datetime, time_sync_endpoint)
     
     
    +
    +
     class TimeSyncEndpoint:
         """A wrapper that uses a TimeSyncClient object to establish and maintain timesync with a robot.
     
    @@ -508,8 +511,8 @@ def _timesync_thread(self):
                 while not self.should_exit:
                     response = self._time_sync_endpoint.response
                     # pylint: disable=no-member
    -                if (not response or response.state.status ==
    -                        time_sync_pb2.TimeSyncState.STATUS_MORE_SAMPLES_NEEDED):
    +                if (not response or response.state.status
    +                        == time_sync_pb2.TimeSyncState.STATUS_MORE_SAMPLES_NEEDED):
                         # No wait between updates while time-sync is not established.
                         pass
                     elif response.state.status == time_sync_pb2.TimeSyncState.STATUS_SERVICE_NOT_READY:
    diff --git a/python/bosdyn-client/src/bosdyn/client/token_manager.py b/python/bosdyn-client/src/bosdyn/client/token_manager.py
    index cd4c38f6d..908298ef0 100644
    --- a/python/bosdyn-client/src/bosdyn/client/token_manager.py
    +++ b/python/bosdyn-client/src/bosdyn/client/token_manager.py
    @@ -61,7 +61,8 @@ def update(self):
                     try:
                         self.robot.authenticate_with_token(self.robot.user_token)
                     except WriteFailedError:
    -                    _LOGGER.exception("Failed to save the token to the cache.  Continuing without caching.")
    +                    _LOGGER.exception(
    +                        "Failed to save the token to the cache.  Continuing without caching.")
                     except (InvalidTokenError, ResponseError, RpcError):
                         _LOGGER.exception("Error refreshing the token.  Retry in %s", retry_interval)
     
    diff --git a/python/bosdyn-client/src/bosdyn/client/util.py b/python/bosdyn-client/src/bosdyn/client/util.py
    index a103caaf8..f4e2742c9 100644
    --- a/python/bosdyn-client/src/bosdyn/client/util.py
    +++ b/python/bosdyn-client/src/bosdyn/client/util.py
    @@ -4,9 +4,11 @@
     # is subject to the terms and conditions of the Boston Dynamics Software
     # Development Kit License (20191101-BDSDK-SL).
     
    +"""Helper functions and classes for creating client applications."""
    +
     from concurrent import futures
     import copy
    -from deprecated.sphinx import deprecated
    +from deprecated import deprecated
     import getpass
     import glob
     import grpc
    @@ -17,17 +19,10 @@
     import time
     import threading
     
    -from bosdyn.api import header_pb2
    -from bosdyn.api import data_acquisition_store_pb2
    -from bosdyn.api import data_buffer_pb2
    -from bosdyn.api import image_pb2
    -from bosdyn.api import local_grid_pb2
    -from bosdyn.api import log_annotation_pb2
    +import bosdyn.client.server_util
     from bosdyn.client.channel import generate_channel_options
    -import bosdyn.util
    -
    -from .auth import InvalidLoginError
    -from .exceptions import Error
    +from bosdyn.client.auth import InvalidLoginError
    +from bosdyn.client.exceptions import Error
     import google.protobuf.descriptor
     
     _LOGGER = logging.getLogger(__name__)
    @@ -123,7 +118,8 @@ def setup_logging(verbose=False, include_dedup_filter=False,
             # main log already. If not, add it to a new handler.
             filter_exists = None
             for handler in logger.handlers:
    -            filter_exists = filter_exists or does_dedup_filter_exist(handler, always_print_logger_levels)
    +            filter_exists = filter_exists or does_dedup_filter_exist(handler,
    +                                                                     always_print_logger_levels)
             if not filter_exists:
                 dedupFilterLog = logging.StreamHandler()
                 # Propagate the filter through the handler. logging.Filter does not propagate to other
    @@ -145,7 +141,9 @@ def does_dedup_filter_exist(logger, always_print_logger_levels):
             Boolean indicating if the DedupLoggingMessages filter already exists and matches the new parameters.
         """
         for filt in logger.filters:
    -        if type(filt) == DedupLoggingMessages and filt.always_print_logger_levels == always_print_logger_levels:
    +        if type(
    +                filt
    +        ) == DedupLoggingMessages and filt.always_print_logger_levels == always_print_logger_levels:
                 return True
         return False
     
    @@ -154,14 +152,6 @@ def get_logger():
         return logging.getLogger()
     
     
    -@deprecated(
    -    reason='App tokens are no longer in use. Authorization is now handled via licenses.',
    -    version='2.0.1',
    -    action="always")
    -def default_app_token_path():
    -    """Do nothing, this method is kept only to maintain backwards compatibility."""
    -    return
    -
     def add_base_arguments(parser):
         """Add hostname argument to parser.
     
    @@ -194,6 +184,7 @@ def add_payload_credentials_arguments(parser, required=True):
         parser.add_argument('--guid', required=required, help='Unique GUID of the payload.')
         parser.add_argument('--secret', required=required, help='Secret of the payload.')
     
    +
     def add_service_hosting_arguments(parser):
         """Add arguments common to most applications hosting a GRPC service.
     
    @@ -205,6 +196,7 @@ def add_service_hosting_arguments(parser):
             ('The port number the service can be reached at (Warning: This port cannot be firewalled).'
              ' Defaults to 0, which will assign an ephemeral port'), type=int)
     
    +
     def add_service_endpoint_arguments(parser):
         """Add arguments common to most applications defining a GRPC service endpoint.
     
    @@ -217,6 +209,15 @@ def add_service_endpoint_arguments(parser):
             ' e.g. "192.168.50.5"')
     
     
    +@deprecated(reason='App tokens are no longer in use. Authorization is now handled via licenses.',
    +            version='2.0.1', action="always")
    +def default_app_token_path():
    +    """Do nothing, this method is kept only to maintain backwards compatibility."""
    +    return
    +
    +
    +@deprecated(reason='The GrpcServiceRunner class helper has moved to server_util.py. Please use '
    +            'bosdyn.client.server_util.GrpcServiceRunner.', version='3.0.0', action="always")
     class GrpcServiceRunner(object):
         """A runner to start a gRPC server on a background thread and allow easy cleanup.
     
    @@ -280,79 +281,57 @@ def run_until_interrupt(self):
     
     
     
    -def populate_response_header(response, request, error_code=header_pb2.CommonError.CODE_OK,
    -                             error_msg=None):
    -    """Sets the ResponseHeader header in the response.
    -    Args:
    -        response (bosdyn.api Response message): The GRPC response message to be populated.
    -        request (bosdyn.api Request message): The header from the request is added to the response.
    -        error_code (header_pb2.CommonError): The status for the RPC response.
    -        error_msg (str): An optional error message describing a bad header status failure.
    -    Returns:
    -        Mutates the response message's header to be fully populated.
    -    """
    -    header = header_pb2.ResponseHeader()
    -    header.request_received_timestamp.CopyFrom(bosdyn.util.now_timestamp())
    -    header.request_header.CopyFrom(request.header)
    -    header.error.code = error_code
    -    if error_msg:
    -        header.error.message = error_msg
    -    copied_request = copy.copy(request)
    -    strip_large_bytes_fields(copied_request)
    -    header.request.Pack(copied_request)
    -    response.header.CopyFrom(header)
    -
    -
    -def strip_large_bytes_fields(proto_message):
    -    message_type = type(proto_message)
    -    whitelist_map = get_bytes_field_whitelist()
    -    if message_type in whitelist_map:
    -        whitelist_map[message_type](proto_message)
    -
    -
    -def get_bytes_field_whitelist():
    -    whitelist_map = {
    -        image_pb2.GetImageResponse : strip_get_image_response,
    -        local_grid_pb2.GetLocalGridsResponse : strip_local_grid_responses,
    -        data_acquisition_store_pb2.StoreDataRequest : strip_store_data_request,
    -        data_acquisition_store_pb2.StoreImageRequest : strip_store_image_request,
    -        data_buffer_pb2.RecordSignalTicksRequest : strip_record_signal_tick,
    -        data_buffer_pb2.RecordDataBlobsRequest : strip_record_data_blob,
    -        log_annotation_pb2.AddLogAnnotationRequest : strip_log_annotation
    -    }
    -    return whitelist_map
    -
    -
    -def strip_image_response(proto_message):
    -    proto_message.shot.image.ClearField("data")
    -
    -def strip_get_image_response(proto_message):
    -    for img_resp in proto_message.image_responses:
    -        strip_image_response(img_resp)
    -
    -
    -def strip_local_grid_responses(proto_message):
    -    for grid_resp in proto_message.local_grid_responses:
    -        grid_resp.local_grid.ClearField("data")
    -
    -
    -def strip_store_image_request(proto_message):
    -    proto_message.image.image.ClearField("data")
    -
    -def strip_store_data_request(proto_message):
    -    proto_message.ClearField("data")
    -
    -
    -def strip_record_signal_tick(proto_message):
    -    for tick_data in proto_message.tick_data:
    -        tick_data.ClearField("data")
    -
    -
    -def strip_record_data_blob(proto_message):
    -    for blob in proto_message.blob_data:
    -        blob.ClearField("data")
    -
    -
    -def strip_log_annotation(proto_message):
    -    for blob in proto_message.annotations.blob_data:
    -        blob.ClearField("data")
    +populate_response_header = deprecated(
    +    reason='The populate_response_header helper has moved to '
    +    'server_util.py. Please use bosdyn.client.server_util.populate_response_header.',
    +    version='3.0.0', action="always")(bosdyn.client.server_util.populate_response_header)
    +
    +strip_large_bytes_fields = deprecated(
    +    reason='The strip_large_bytes_fields helper has moved to '
    +    'server_util.py. Please use bosdyn.client.server_util.strip_large_bytes_fields.',
    +    version='3.0.0', action="always")(bosdyn.client.server_util.strip_large_bytes_fields)
    +
    +get_bytes_field_whitelist = deprecated(
    +    reason='The get_bytes_field_whitelist helper has moved to '
    +    'server_util.py. Please use bosdyn.client.server_util.get_bytes_field_allowlist.',
    +    version='3.0.0', action="always")(bosdyn.client.server_util.get_bytes_field_allowlist)
    +
    +strip_image_response = deprecated(
    +    reason='The strip_image_response helper has moved to '
    +    'server_util.py. Please use bosdyn.client.server_util.strip_image_response.', version='3.0.0',
    +    action="always")(bosdyn.client.server_util.strip_image_response)
    +
    +strip_get_image_response = deprecated(
    +    reason='The strip_get_image_response helper has moved to '
    +    'server_util.py. Please use bosdyn.client.server_util.strip_get_image_response.',
    +    version='3.0.0', action="always")(bosdyn.client.server_util.strip_get_image_response)
    +
    +strip_local_grid_responses = deprecated(
    +    reason='The strip_local_grid_responses helper has moved to '
    +    'server_util.py. Please use bosdyn.client.server_util.strip_local_grid_responses.',
    +    version='3.0.0', action="always")(bosdyn.client.server_util.strip_local_grid_responses)
    +
    +strip_store_image_request = deprecated(
    +    reason='The strip_store_image_request helper has moved to '
    +    'server_util.py. Please use bosdyn.client.server_util.strip_store_image_request.',
    +    version='3.0.0', action="always")(bosdyn.client.server_util.strip_store_image_request)
    +
    +strip_store_data_request = deprecated(
    +    reason='The strip_store_data_request helper has moved to '
    +    'server_util.py. Please use bosdyn.client.server_util.strip_store_data_request.',
    +    version='3.0.0', action="always")(bosdyn.client.server_util.strip_store_data_request)
    +
    +strip_record_signal_tick = deprecated(
    +    reason='The strip_record_signal_tick helper has moved to '
    +    'server_util.py. Please use bosdyn.client.server_util.strip_record_signal_tick.',
    +    version='3.0.0', action="always")(bosdyn.client.server_util.strip_record_signal_tick)
    +
    +strip_record_data_blob = deprecated(
    +    reason='The strip_record_data_blob helper has moved to '
    +    'server_util.py. Please use bosdyn.client.server_util.strip_record_data_blob.', version='3.0.0',
    +    action="always")(bosdyn.client.server_util.strip_record_data_blob)
    +
    +strip_log_annotation = deprecated(
    +    reason='The strip_log_annotation helper has moved to '
    +    'server_util.py. Please use bosdyn.client.server_util.strip_log_annotation.', version='3.0.0',
    +    action="always")(bosdyn.client.server_util.strip_log_annotation)
    diff --git a/python/bosdyn-client/tests/helpers.py b/python/bosdyn-client/tests/helpers.py
    index 335471a02..fae6330b6 100644
    --- a/python/bosdyn-client/tests/helpers.py
    +++ b/python/bosdyn-client/tests/helpers.py
    @@ -11,7 +11,6 @@
     
     import bosdyn.api.header_pb2 as HeaderProto
     
    -
     def setup_client_and_service(client, service, service_adder):
         """Starts a service listening on a port and points client to it.
     
    diff --git a/python/bosdyn-client/tests/test_arm_surface_contact.py b/python/bosdyn-client/tests/test_arm_surface_contact.py
    index 5d3a72ad0..23661114a 100644
    --- a/python/bosdyn-client/tests/test_arm_surface_contact.py
    +++ b/python/bosdyn-client/tests/test_arm_surface_contact.py
    @@ -7,13 +7,14 @@
     """Tests for the arm surface contact client."""
     import pytest
     
    -from bosdyn.client.robot_command import  _edit_proto
    +from bosdyn.client.robot_command import _edit_proto
     from bosdyn.client.arm_surface_contact import EDIT_TREE_CONVERT_LOCAL_TIME_TO_ROBOT_TIME
     from bosdyn.api import arm_surface_contact_service_pb2
     from google.protobuf import timestamp_pb2
     
     
     def test_edit_timestamps():
    +
         def _set_new_time(key, proto):
             """If proto has a field named key, fill set it to end_time_secs as robot time. """
             if key not in proto.DESCRIPTOR.fields_by_name:
    diff --git a/python/bosdyn-client/tests/test_auto_return_client.py b/python/bosdyn-client/tests/test_auto_return_client.py
    new file mode 100644
    index 000000000..40c52cf6a
    --- /dev/null
    +++ b/python/bosdyn-client/tests/test_auto_return_client.py
    @@ -0,0 +1,90 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""Test the client to the auto_return service."""
    +import concurrent
    +import grpc
    +import pytest
    +
    +from bosdyn.api.auto_return import auto_return_pb2
    +from bosdyn.api.auto_return import auto_return_service_pb2_grpc
    +
    +import bosdyn.client.auto_return
    +from . import helpers
    +
    +
    +class MockAutoReturnServicer(auto_return_service_pb2_grpc.AutoReturnServiceServicer):
    +
    +    def __init__(self):
    +        super(MockAutoReturnServicer, self).__init__()
    +        self.active_configuration_request = None
    +        self.leases = None
    +
    +    def GetConfiguration(self, request, context):
    +        response = auto_return_pb2.GetConfigurationResponse()
    +        helpers.add_common_header(response, request)
    +        if self.active_configuration_request:
    +            response.request.CopyFrom(self.active_configuration_request)
    +            response.enabled = True
    +        return response
    +
    +    def Configure(self, request, context):
    +        response = auto_return_pb2.ConfigureResponse()
    +        helpers.add_common_header(response, request)
    +        if request.params.max_displacement <= 0:
    +            response.invalid_params.max_displacement = request.params.max_displacement
    +            response.status = auto_return_pb2.ConfigureResponse.STATUS_INVALID_PARAMS
    +        else:
    +            response.status = auto_return_pb2.ConfigureResponse.STATUS_OK
    +            self.active_configuration_request = request
    +            self.leases = request.leases
    +        return response
    +
    +
    +@pytest.fixture(scope='function')
    +def client():
    +    return bosdyn.client.auto_return.AutoReturnClient()
    +
    +
    +@pytest.fixture(scope='function')
    +def service():
    +    return MockAutoReturnServicer()
    +
    +
    +@pytest.fixture(scope='function')
    +def server(client, service):
    +    server = grpc.server(concurrent.futures.ThreadPoolExecutor(max_workers=1))
    +    auto_return_service_pb2_grpc.add_AutoReturnServiceServicer_to_server(service, server)
    +    port = server.add_insecure_port('localhost:0')
    +    channel = grpc.insecure_channel('localhost:{}'.format(port))
    +    client.channel = channel
    +    server.start()
    +    return server
    +
    +
    +def test_simple(client, server, service):
    +    """Test basic usage of the client."""
    +    client.get_configuration()
    +    resp = client.get_configuration_async().result()
    +    assert not resp.enabled
    +    assert not resp.HasField('request')
    +
    +    params = auto_return_pb2.Params()
    +    params.max_displacement = -1
    +    with pytest.raises(bosdyn.client.auto_return.InvalidParameterError):
    +        client.configure(params, leases=[])
    +    with pytest.raises(bosdyn.client.auto_return.InvalidParameterError):
    +        client.configure_async(params, leases=[])
    +
    +    params.max_displacement = 12
    +    client.configure(params, leases=[])
    +    assert service.active_configuration_request.params.SerializeToString(
    +    ) == params.SerializeToString()
    +    # Test that the NoneType was overwritten with an iterable.
    +    assert len(service.leases) == 0
    +
    +    resp = client.get_configuration()
    +    assert resp.request.params.SerializeToString() == params.SerializeToString()
    diff --git a/python/bosdyn-client/tests/test_bddf.py b/python/bosdyn-client/tests/test_bddf.py
    index 55642bf2f..78b7e94b9 100644
    --- a/python/bosdyn-client/tests/test_bddf.py
    +++ b/python/bosdyn-client/tests/test_bddf.py
    @@ -14,9 +14,9 @@
     
     import bosdyn.api.bddf_pb2 as bddf
     from bosdyn.api.data_buffer_pb2 import OperatorComment
    -from bosdyn.client.bddf import (DataReader, DataWriter, PodSeriesReader, PodSeriesWriter,
    -                                ProtoSeriesWriter, ProtobufChannelReader, ProtobufReader,
    -                                StreamDataReader)
    +from bosdyn.bddf import (DataReader, DataWriter, PodSeriesReader, PodSeriesWriter,
    +                         ProtobufSeriesWriter, ProtobufChannelReader, ProtobufReader,
    +                         StreamDataReader)
     from bosdyn.util import now_nsec, now_timestamp, timestamp_to_nsec
     
     
    @@ -45,14 +45,12 @@ def test_write_read():
             data_writer.write_data(series1_index, timestamp_nsec, msg_data, [1, 2])
     
             # Write a protobuf to the file.
    -        proto_writer = ProtoSeriesWriter(data_writer, OperatorComment)
    +        proto_writer = ProtobufSeriesWriter(data_writer, OperatorComment)
             proto_writer.write(timestamp_to_nsec(operator_comment.timestamp), operator_comment)
     
             # Write POD data (floats) to the file.
             pod_writer = PodSeriesWriter(data_writer, pod_series_type, pod_spec, bddf.TYPE_FLOAT32,
    -                                     annotations={
    -                                         'units': 'm/s^2'
    -                                     })
    +                                     annotations={'units': 'm/s^2'})
             for val in range(10, 20):
                 pod_writer.write(timestamp_nsec, val)
     
    diff --git a/python/bosdyn-client/tests/test_cmdline.py b/python/bosdyn-client/tests/test_cmdline.py
    index a286aa1c3..0fd85561a 100644
    --- a/python/bosdyn-client/tests/test_cmdline.py
    +++ b/python/bosdyn-client/tests/test_cmdline.py
    @@ -10,5 +10,5 @@
     
     
     def test_null():
    -    '''This at least confirms that all the modules listed above load successfully.'''
    +    """This at least confirms that all the modules listed above load successfully."""
         pass
    diff --git a/python/bosdyn-client/tests/test_directory_registration.py b/python/bosdyn-client/tests/test_directory_registration.py
    index 093970a49..7365326ae 100644
    --- a/python/bosdyn-client/tests/test_directory_registration.py
    +++ b/python/bosdyn-client/tests/test_directory_registration.py
    @@ -187,7 +187,7 @@ def test_keep_alive(default_service_entry, default_service_endpoint):
         name = default_service_entry.name
         assert name not in service.service_entries
         with keepalive.start(name, default_service_entry.type, default_service_entry.authority,
    -                    default_service_endpoint.host_ip, default_service_endpoint.port):
    +                         default_service_endpoint.host_ip, default_service_endpoint.port):
             assert name in service.service_entries
     
             # Just have some statement inside the "with" context.
    @@ -212,7 +212,7 @@ def test_keep_alive_update(default_service_entry, default_service_endpoint):
     
         new_authority = default_service_entry.authority + 'woo-hoo'
         with keepalive.start(name, default_service_entry.type, new_authority,
    -                    default_service_endpoint.host_ip, default_service_endpoint.port):
    +                         default_service_endpoint.host_ip, default_service_endpoint.port):
             assert service.service_entries[name].authority == new_authority
     
             # Make sure the thread is still alive after a few loops.
    diff --git a/python/bosdyn-client/tests/test_fault.py b/python/bosdyn-client/tests/test_fault.py
    index 193363f15..b45024aef 100644
    --- a/python/bosdyn-client/tests/test_fault.py
    +++ b/python/bosdyn-client/tests/test_fault.py
    @@ -30,6 +30,7 @@ def test_trigger_service_fault_error():
         response.status = response.STATUS_OK
         assert not _trigger_service_fault_error(response)
     
    +
     def test_clear_service_fault_error():
         # Test unset header error
         response = service_fault_pb2.ClearServiceFaultResponse()
    diff --git a/python/bosdyn-client/tests/test_frame_helpers.py b/python/bosdyn-client/tests/test_frame_helpers.py
    index f3a9d337f..11ce6c433 100644
    --- a/python/bosdyn-client/tests/test_frame_helpers.py
    +++ b/python/bosdyn-client/tests/test_frame_helpers.py
    @@ -12,6 +12,7 @@
     import pytest
     import math
     
    +
     def _create_snapshot(frame_tree_snapshot_string):
         frame_tree_snapshot = geom_protos.FrameTreeSnapshot()
         google.protobuf.text_format.Parse(frame_tree_snapshot_string, frame_tree_snapshot)
    @@ -541,12 +542,14 @@ def test_get_a_tform_b_se2():
         assert odom_tform_body is None
     
         # Check that a gravity aligned frame is used and properly computed.
    -    vision_tform_fiducial_404 = frame_helpers.get_se2_a_tform_b(frame_tree, "vision", "fiducial_404")
    +    vision_tform_fiducial_404 = frame_helpers.get_se2_a_tform_b(frame_tree, "vision",
    +                                                                "fiducial_404")
         assert vision_tform_fiducial_404 is not None
         assert math.fabs(vision_tform_fiducial_404.position.x - 4) < 1e-6
         assert math.fabs(vision_tform_fiducial_404.position.y) < 1e-6
         assert math.fabs(vision_tform_fiducial_404.angle) < 1e-6
     
    +
     def test_express_velocity_new_frame():
         snapshot_text = """
         child_to_parent_edge_map {
    @@ -620,23 +623,85 @@ def test_express_velocity_new_frame():
         assert frame_helpers.validate_frame_tree_snapshot(frame_tree)
     
         # Transform SE(2) velocity
    -    vel_of_body_in_vision = math_helpers.SE2Velocity(1,1,2)
    -    vel_of_body_in_odom = frame_helpers.express_se2_velocity_in_new_frame(frame_tree, "vision", "odom", vel_of_body_in_vision)
    +    vel_of_body_in_vision = math_helpers.SE2Velocity(1, 1, 2)
    +    vel_of_body_in_odom = frame_helpers.express_se2_velocity_in_new_frame(
    +        frame_tree, "vision", "odom", vel_of_body_in_vision)
         assert vel_of_body_in_odom is not None
         assert type(vel_of_body_in_vision) == math_helpers.SE2Velocity
    -    assert math.fabs(vel_of_body_in_odom.angular -2) < 1e-6
    -    assert math.fabs(vel_of_body_in_odom.linear.x -1) < 1e-6
    -    assert math.fabs(vel_of_body_in_odom.linear.y -5) < 1e-6
    -
    +    assert math.fabs(vel_of_body_in_odom.angular - 2) < 1e-6
    +    assert math.fabs(vel_of_body_in_odom.linear.x - 1) < 1e-6
    +    assert math.fabs(vel_of_body_in_odom.linear.y - 5) < 1e-6
     
         # Transform SE(3) velocity
    -    vel_of_body_in_vision = math_helpers.SE3Velocity(1,2,3,1,2,3)
    -    vel_of_body_in_odom = frame_helpers.express_se3_velocity_in_new_frame(frame_tree, "vision", "odom", vel_of_body_in_vision)
    +    vel_of_body_in_vision = math_helpers.SE3Velocity(1, 2, 3, 1, 2, 3)
    +    vel_of_body_in_odom = frame_helpers.express_se3_velocity_in_new_frame(
    +        frame_tree, "vision", "odom", vel_of_body_in_vision)
         assert vel_of_body_in_odom is not None
         assert type(vel_of_body_in_vision) == math_helpers.SE3Velocity
    -    assert math.fabs(vel_of_body_in_odom.angular.x -1) < 1e-6
    -    assert math.fabs(vel_of_body_in_odom.angular.y -2) < 1e-6
    -    assert math.fabs(vel_of_body_in_odom.angular.z -3) < 1e-6
    -    assert math.fabs(vel_of_body_in_odom.linear.x -21) < 1e-6
    -    assert math.fabs(vel_of_body_in_odom.linear.y -(-2)) < 1e-6
    -    assert math.fabs(vel_of_body_in_odom.linear.z -(-1)) < 1e-6
    +    assert math.fabs(vel_of_body_in_odom.angular.x - 1) < 1e-6
    +    assert math.fabs(vel_of_body_in_odom.angular.y - 2) < 1e-6
    +    assert math.fabs(vel_of_body_in_odom.angular.z - 3) < 1e-6
    +    assert math.fabs(vel_of_body_in_odom.linear.x - 21) < 1e-6
    +    assert math.fabs(vel_of_body_in_odom.linear.y - (-2)) < 1e-6
    +    assert math.fabs(vel_of_body_in_odom.linear.z - (-1)) < 1e-6
    +
    +def test_express_velocity_types():
    +    snapshot_text = """
    +    child_to_parent_edge_map {
    +      key: "vision"
    +      value: {
    +        parent_frame_name: "body"
    +        parent_tform_child: {
    +          position: {
    +            x: 1
    +            z: 10
    +          }
    +          rotation: {
    +            w: 1
    +          }
    +        }
    +      }
    +    }
    +    child_to_parent_edge_map {
    +      key: "body"
    +      value: {
    +        parent_frame_name: ""
    +      }
    +    }
    +    """
    +    frame_tree = _create_snapshot(snapshot_text)
    +    assert frame_helpers.validate_frame_tree_snapshot(frame_tree)
    +    test_vel1 = math_helpers.SE3Velocity(1.1,2.2,3.3,4.4,5.5,6.6)
    +    assert type(test_vel1.linear_velocity_x) == float
    +    assert test_vel1.linear_velocity_x == 1.1
    +    assert test_vel1.linear.x == 1.1
    +    test_vel2 = math_helpers.SE3Velocity(1.1,2.2,3.3,4.4,5.5,6.6)
    +    test_vel2_proto = test_vel2.to_proto()
    +
    +    body_vel = frame_helpers.express_se3_velocity_in_new_frame(frame_tree, "body", "vision", test_vel2)
    +    assert body_vel is not None
    +    assert type(body_vel.linear.x) == float
    +    assert type(body_vel.linear_velocity_x) == float
    +    assert body_vel.linear_velocity_x == 56.1
    +    assert body_vel.linear.x == 56.1
    +    body_vel = frame_helpers.express_se3_velocity_in_new_frame(frame_tree, "body", "vision", test_vel2_proto)
    +    assert body_vel is not None
    +    assert type(body_vel.linear.x) == float
    +    assert type(body_vel.linear_velocity_x) == float
    +    assert body_vel.linear_velocity_x == 56.1
    +    assert body_vel.linear.x == 56.1
    +
    +    test_vel3 = math_helpers.SE2Velocity(1.1,2.2,3.3)
    +    test_vel3_proto = test_vel3.to_proto()
    +    body_vel = frame_helpers.express_se2_velocity_in_new_frame(frame_tree, "body", "vision", test_vel3)
    +    assert body_vel is not None
    +    assert type(body_vel.linear.x) == float
    +    assert type(body_vel.linear_velocity_x) == float
    +    assert body_vel.linear_velocity_x == 1.1
    +    assert body_vel.linear.x == 1.1
    +    body_vel = frame_helpers.express_se2_velocity_in_new_frame(frame_tree, "body", "vision", test_vel3_proto)
    +    assert body_vel is not None
    +    assert type(body_vel.linear.x) == float
    +    assert type(body_vel.linear_velocity_x) == float
    +    assert body_vel.linear_velocity_x == 1.1
    +    assert body_vel.linear.x == 1.1
    \ No newline at end of file
    diff --git a/python/bosdyn-client/tests/test_graph_nav_client.py b/python/bosdyn-client/tests/test_graph_nav_client.py
    index 504437c52..d8edc814e 100644
    --- a/python/bosdyn-client/tests/test_graph_nav_client.py
    +++ b/python/bosdyn-client/tests/test_graph_nav_client.py
    @@ -16,6 +16,7 @@
     from bosdyn.client.exceptions import UnsetStatusError, InternalServerError
     from bosdyn.client.time_sync import TimeSyncEndpoint
     
    +
     class MockGraphNavServicer(graph_nav_service_pb2_grpc.GraphNavServiceServicer):
         """GraphNav servicer for testing.
     
    @@ -26,11 +27,16 @@ def __init__(self):
             super(MockGraphNavServicer, self).__init__()
             self.common_header_code = header_pb2.CommonError.CODE_OK
             self.nav_feedback_status = graph_nav_pb2.NavigationFeedbackResponse.STATUS_REACHED_GOAL
    -        self.nav_to_resp = graph_nav_pb2.NavigateToResponse(status=graph_nav_pb2.NavigateToResponse.STATUS_OK)
    -        self.nav_route_resp = graph_nav_pb2.NavigateRouteResponse(status=graph_nav_pb2.NavigateRouteResponse.STATUS_OK)
    +        self.nav_to_resp = graph_nav_pb2.NavigateToResponse(
    +            status=graph_nav_pb2.NavigateToResponse.STATUS_OK)
    +        self.nav_route_resp = graph_nav_pb2.NavigateRouteResponse(
    +            status=graph_nav_pb2.NavigateRouteResponse.STATUS_OK)
             self.upload_waypoint_resp = graph_nav_pb2.UploadWaypointSnapshotResponse()
             self.upload_edge_resp = graph_nav_pb2.UploadEdgeSnapshotResponse()
    -        self.set_loc_resp = graph_nav_pb2.SetLocalizationResponse(status=graph_nav_pb2.SetLocalizationResponse.STATUS_OK)
    +        self.set_loc_resp = graph_nav_pb2.SetLocalizationResponse(
    +            status=graph_nav_pb2.SetLocalizationResponse.STATUS_OK)
    +        self.upload_graph_resp = graph_nav_pb2.UploadGraphResponse(
    +            status=graph_nav_pb2.UploadGraphResponse.STATUS_OK)
             self.download_wp_snapshot_status = graph_nav_pb2.DownloadWaypointSnapshotResponse.STATUS_OK
             self.download_edge_snapshot_status = graph_nav_pb2.DownloadEdgeSnapshotResponse.STATUS_OK
             self.lease_use_result = None
    @@ -68,6 +74,7 @@ def ClearGraph(self, request, context):
     
         def UploadGraph(self, request, context):
             resp = graph_nav_pb2.UploadGraphResponse()
    +        resp.CopyFrom(self.upload_graph_resp)
             resp.header.error.code = self.common_header_code
             if self.lease_use_result:
                 resp.lease_use_result.CopyFrom(self.lease_use_result)
    @@ -106,6 +113,7 @@ def DownloadEdgeSnapshot(self, request, context):
             resp.status = self.download_edge_snapshot_status
             yield resp
     
    +
     @pytest.fixture
     def client(time_sync):
         c = GraphNavClient()
    @@ -117,13 +125,15 @@ def client(time_sync):
     def service():
         return MockGraphNavServicer()
     
    +
     @pytest.fixture
     def time_sync():
    -    ts =  TimeSyncEndpoint(None)
    +    ts = TimeSyncEndpoint(None)
         ts._locked_previous_response = time_sync_pb2.TimeSyncUpdateResponse()
         ts.response.state.status = time_sync_pb2.TimeSyncState.STATUS_OK
         return ts
     
    +
     @pytest.fixture
     def server(client, service):
         server = grpc.server(concurrent.futures.ThreadPoolExecutor(max_workers=1))
    @@ -135,6 +145,7 @@ def server(client, service):
         yield server
         server.stop(0)
     
    +
     @pytest.mark.parametrize('func', ('navigation_feedback_async', 'navigation_feedback'))
     def test_feedback_exceptions(client, service, server, func):
         """Client's navigation feedback should provide expected exceptions/responses."""
    @@ -168,11 +179,12 @@ def test_feedback_exceptions(client, service, server, func):
     
     
     def test_navigate_to_exceptions(client, service, server):
    -    make_call = lambda:client.navigate_to('somewhere-id', 2.0)
    +    make_call = lambda: client.navigate_to('somewhere-id', 2.0)
         cmd_id = make_call()
         assert type(cmd_id) is int
     
    -    service.lease_use_result = lease_pb2.LeaseUseResult(status=lease_pb2.LeaseUseResult.STATUS_OLDER)
    +    service.lease_use_result = lease_pb2.LeaseUseResult(
    +        status=lease_pb2.LeaseUseResult.STATUS_OLDER)
         with pytest.raises(bosdyn.client.LeaseUseError):
             make_call()
     
    @@ -226,10 +238,11 @@ def test_navigate_to_exceptions(client, service, server):
     
     
     def test_navigate_route_exceptions(client, service, server):
    -    make_call = lambda:client.navigate_route(nav_pb2.Route(), 2.0)
    +    make_call = lambda: client.navigate_route(nav_pb2.Route(), 2.0)
         cmd_id = make_call()
         assert cmd_id == 0
    -    service.lease_use_result = lease_pb2.LeaseUseResult(status=lease_pb2.LeaseUseResult.STATUS_OLDER)
    +    service.lease_use_result = lease_pb2.LeaseUseResult(
    +        status=lease_pb2.LeaseUseResult.STATUS_OLDER)
         with pytest.raises(bosdyn.client.LeaseUseError):
             make_call()
     
    @@ -258,6 +271,9 @@ def test_navigate_route_exceptions(client, service, server):
             make_call()
     
         service.nav_route_resp.status = service.nav_route_resp.STATUS_UNKNOWN_ROUTE_ELEMENTS
    +    with pytest.raises(bosdyn.client.graph_nav.UnknownRouteElementsError):
    +        make_call()
    +    #make sure the misspelled error works for backwards compatibility.
         with pytest.raises(bosdyn.client.graph_nav.UnkownRouteElementsError):
             make_call()
     
    @@ -285,12 +301,14 @@ def test_navigate_route_exceptions(client, service, server):
         with pytest.raises(bosdyn.client.graph_nav.RouteNotUpdatingError):
             make_call()
     
    +
     def test_clear_graph(client, service, server):
         make_call = lambda: client.clear_graph()
     
         make_call()
     
    -    service.lease_use_result = lease_pb2.LeaseUseResult(status=lease_pb2.LeaseUseResult.STATUS_OLDER)
    +    service.lease_use_result = lease_pb2.LeaseUseResult(
    +        status=lease_pb2.LeaseUseResult.STATUS_OLDER)
         with pytest.raises(bosdyn.client.LeaseUseError):
             make_call()
     
    @@ -299,31 +317,49 @@ def test_upload_graph_exceptions(client, service, server):
         make_call = lambda: client.upload_graph(graph=map_pb2.Graph())
         make_call()
     
    -    service.lease_use_result = lease_pb2.LeaseUseResult(status=lease_pb2.LeaseUseResult.STATUS_OLDER)
    +    service.lease_use_result = lease_pb2.LeaseUseResult(
    +        status=lease_pb2.LeaseUseResult.STATUS_OLDER)
         with pytest.raises(bosdyn.client.LeaseUseError):
             make_call()
     
    +    service.lease_use_result = lease_pb2.LeaseUseResult(status=lease_pb2.LeaseUseResult.STATUS_OK)
    +    make_call()
    +
    +    service.upload_graph_resp.status = service.upload_graph_resp.STATUS_MAP_TOO_LARGE_LICENSE
    +    with pytest.raises(bosdyn.client.graph_nav.MapTooLargeLicenseError):
    +        make_call()
    +
    +    service.upload_graph_resp.status = service.upload_graph_resp.STATUS_INVALID_GRAPH
    +    with pytest.raises(bosdyn.client.graph_nav.InvalidGraphError):
    +        make_call()
    +
    +
     def test_upload_waypoint_exceptions(client, service, server):
         make_call = lambda: client.upload_waypoint_snapshot(map_pb2.WaypointSnapshot())
         make_call()
     
    -    service.lease_use_result = lease_pb2.LeaseUseResult(status=lease_pb2.LeaseUseResult.STATUS_OLDER)
    +    service.lease_use_result = lease_pb2.LeaseUseResult(
    +        status=lease_pb2.LeaseUseResult.STATUS_OLDER)
         with pytest.raises(bosdyn.client.LeaseUseError):
             make_call()
     
    +
     def test_upload_edge_exceptions(client, service, server):
         make_call = lambda: client.upload_edge_snapshot(map_pb2.EdgeSnapshot())
         make_call()
     
    -    service.lease_use_result = lease_pb2.LeaseUseResult(status=lease_pb2.LeaseUseResult.STATUS_OLDER)
    +    service.lease_use_result = lease_pb2.LeaseUseResult(
    +        status=lease_pb2.LeaseUseResult.STATUS_OLDER)
         with pytest.raises(bosdyn.client.LeaseUseError):
             make_call()
     
    +
     def test_set_localization_exceptions(client, service, server):
         make_call = lambda: client.set_localization(nav_pb2.Localization())
         make_call()
     
    -    service.lease_use_result = lease_pb2.LeaseUseResult(status=lease_pb2.LeaseUseResult.STATUS_OLDER)
    +    service.lease_use_result = lease_pb2.LeaseUseResult(
    +        status=lease_pb2.LeaseUseResult.STATUS_OLDER)
         with pytest.raises(bosdyn.client.LeaseUseError):
             make_call()
     
    @@ -346,6 +382,7 @@ def test_set_localization_exceptions(client, service, server):
         with pytest.raises(bosdyn.client.graph_nav.RequestFailedError):
             make_call()
     
    +
     def test_download_waypoint_snapshot(client, service, server):
         make_call = lambda: client.download_waypoint_snapshot(waypoint_snapshot_id="mywaypoint")
         make_call()
    @@ -359,6 +396,7 @@ def test_download_waypoint_snapshot(client, service, server):
         with pytest.raises(bosdyn.client.graph_nav.UnknownMapInformationError):
             make_call()
     
    +
     def test_download_edge_snapshot(client, service, server):
         make_call = lambda: client.download_edge_snapshot(edge_snapshot_id="myedge")
         make_call()
    diff --git a/python/bosdyn-client/tests/test_image_service_helpers.py b/python/bosdyn-client/tests/test_image_service_helpers.py
    index 9a72343fc..0783133f6 100644
    --- a/python/bosdyn-client/tests/test_image_service_helpers.py
    +++ b/python/bosdyn-client/tests/test_image_service_helpers.py
    @@ -13,14 +13,15 @@
     from bosdyn.api import service_fault_pb2
     from bosdyn.api import image_pb2, header_pb2
     from bosdyn.client.fault import FaultClient
    -from bosdyn.client.image_service_helpers import (VisualImageSource, ImageCaptureThread, CameraBaseImageServicer,
    -                                                CameraInterface)
    +from bosdyn.client.image_service_helpers import (VisualImageSource, ImageCaptureThread,
    +                                                 CameraBaseImageServicer, CameraInterface)
     from google.protobuf import timestamp_pb2
     
    +
     class MockFaultClient:
     
         def __init__(self):
    -        self.service_fault_counts = dict() # key=fault id name, value = count
    +        self.service_fault_counts = dict()  # key=fault id name, value = count
     
         def trigger_service_fault_async(self, service_fault, **kwargs):
             fault = service_fault.fault_id.fault_name
    @@ -31,7 +32,7 @@ def trigger_service_fault_async(self, service_fault, **kwargs):
             return service_fault_pb2.TriggerServiceFaultResponse()
     
         def clear_service_fault_async(self, service_fault_id, clear_all_service_faults=False,
    -                            clear_all_payload_faults=False, **kwargs):
    +                                  clear_all_payload_faults=False, **kwargs):
             # This function makes the assumption that every fault in this mock fault client's dictionary
             # has the same service_name in the fault id.
             if clear_all_service_faults:
    @@ -46,14 +47,16 @@ def get_total_fault_count(self):
                 fault_amount += fault_count
             return fault_amount
     
    +
     class MockTimeSync:
     
         def wait_for_sync(self):
             return 1
     
    -    def robot_timestamp_from_local_secs(self,seconds):
    +    def robot_timestamp_from_local_secs(self, seconds):
             return timestamp_pb2.Timestamp(seconds=10, nanos=20)
     
    +
     class MockRobot:
     
         def __init__(self, token=None):
    @@ -68,32 +71,49 @@ def ensure_client(self, name):
         def time_sync(self):
             return MockTimeSync()
     
    +
     ##### Helper functions to mimic the function signatures of the capture function
     # and the decode image function for the VisualImageSource.
     class FakeCamera(CameraInterface):
    +
         def __init__(self, capture_func, decode_func):
             self.capture_func = capture_func
             self.decode_func = decode_func
    +
         def blocking_capture(self):
             return self.capture_func()
    +
         def image_decode(self, image_data, image_proto, image_format, quality_percent):
             return self.decode_func(image_data, image_proto, image_format, quality_percent)
     
    +
     def capture_fake():
         return "image", 1
    +
    +
     def decode_fake(img_data, img_proto, img_format, quality):
         img_proto.rows = 15
    +
    +
     def capture_return_onething():
         return 1
    +
    +
     def decode_less_args(arg1, arg2):
         return 2
    +
    +
     def capture_with_error():
         raise Exception("Failed Capture.")
    +
    +
     def decode_with_error(img_data, img_proto, img_format, quality):
         img_proto.rows = 15
         raise Exception("Failed Decode.")
     
    +
     class Increment():
    +
         def __init__(self, barrier, t=1.1):
             self.timestamp = t
             self.barrier = barrier
    @@ -102,13 +122,15 @@ def capture_increment_count(self):
             self.barrier.wait(timeout=1)
             return "image", self.timestamp
     
    +
     def test_faults_in_visual_source():
         # Create the fault client
         fault_client = MockFaultClient()
     
         # Populate fault client with at least one "old" fault.
         fault_client.trigger_service_fault_async(
    -        service_fault_pb2.ServiceFault(fault_id=service_fault_pb2.ServiceFaultId(fault_name="fault1")))
    +        service_fault_pb2.ServiceFault(fault_id=service_fault_pb2.ServiceFaultId(
    +            fault_name="fault1")))
         init_fault_amount = fault_client.get_total_fault_count()
     
         visual_src = VisualImageSource("source1", FakeCamera(capture_with_error, decode_with_error))
    @@ -137,16 +159,19 @@ def test_faults_in_visual_source():
         image, timestamp = visual_src.get_image_and_timestamp()
         assert image is None
         assert timestamp is None
    -    assert fault_client.service_fault_counts[visual_src.camera_capture_fault.fault_id.fault_name] == 1
    +    assert fault_client.service_fault_counts[
    +        visual_src.camera_capture_fault.fault_id.fault_name] == 1
         im_proto = image_pb2.Image(rows=21)
         success = visual_src.image_decode_with_error_checking(None, im_proto, None, None)
         assert im_proto.rows == 15
         assert fault_client.service_fault_counts[visual_src.decode_data_fault.fault_id.fault_name] == 1
         assert not success
     
    +
     def test_faults_are_cleared_on_success():
         # Check that captures/decodes that fail and then later succeed will cause the faults to get cleared.
         class FailsThenSucceeds(CameraInterface):
    +
             def __init__(self):
                 self.capture_count = 0
                 self.decode_count = 0
    @@ -167,16 +192,21 @@ def image_decode(self, image_data, image_proto, image_format, quality_percent):
         visual_src.initialize_faults(fault_client, "service1")
         # The first call to the capture and decode functions cause a fault to be thrown.
         image, timestamp = visual_src.get_image_and_timestamp()
    -    assert fault_client.service_fault_counts[visual_src.camera_capture_fault.fault_id.fault_name] == 1
    -    success = visual_src.image_decode_with_error_checking(None, image_pb2.Image(rows=21), None, None)
    +    assert fault_client.service_fault_counts[
    +        visual_src.camera_capture_fault.fault_id.fault_name] == 1
    +    success = visual_src.image_decode_with_error_checking(None, image_pb2.Image(rows=21), None,
    +                                                          None)
         assert fault_client.service_fault_counts[visual_src.decode_data_fault.fault_id.fault_name] == 1
     
         # The second calls will succeed, and now cause the faults to be cleared.
         image, timestamp = visual_src.get_image_and_timestamp()
    -    assert fault_client.service_fault_counts[visual_src.camera_capture_fault.fault_id.fault_name] == 0
    -    success = visual_src.image_decode_with_error_checking(None, image_pb2.Image(rows=21), None, None)
    +    assert fault_client.service_fault_counts[
    +        visual_src.camera_capture_fault.fault_id.fault_name] == 0
    +    success = visual_src.image_decode_with_error_checking(None, image_pb2.Image(rows=21), None,
    +                                                          None)
         assert fault_client.service_fault_counts[visual_src.decode_data_fault.fault_id.fault_name] == 0
     
    +
     def test_make_image_source():
         # Create a visual source with no rows/cols/image type provided.
         src_name = "source1"
    @@ -190,7 +220,8 @@ def test_make_image_source():
         src_name2 = "source2"
         src_rows2 = 60
         src_cols2 = 100
    -    visual_src2 = VisualImageSource(src_name2, FakeCamera(capture_fake, decode_fake), src_rows2, src_cols2)
    +    visual_src2 = VisualImageSource(src_name2, FakeCamera(capture_fake, decode_fake), src_rows2,
    +                                    src_cols2)
         assert visual_src2.image_source_proto.name == src_name2
         assert visual_src2.image_source_proto.image_type == image_pb2.ImageSource.IMAGE_TYPE_VISUAL
         assert visual_src2.image_source_proto.rows == src_rows2
    @@ -207,6 +238,7 @@ def test_make_image_source():
         assert img_proto.rows == src_rows3
         assert img_proto.cols == src_cols3
     
    +
     def test_make_capture_params():
         # Create a visual source with no gain/exposure provided.
         visual_src = VisualImageSource("source1", FakeCamera(capture_fake, decode_fake))
    @@ -218,7 +250,8 @@ def test_make_capture_params():
         # Create a visual source with gain and exposure provided.
         gain = 1.5
         exposure = 101.005
    -    visual_src = VisualImageSource("source1", FakeCamera(capture_fake, decode_fake), gain=gain, exposure=exposure)
    +    visual_src = VisualImageSource("source1", FakeCamera(capture_fake, decode_fake), gain=gain,
    +                                   exposure=exposure)
         params = visual_src.get_image_capture_params()
         assert abs(params.gain - gain) < 1e-3
         assert abs(params.exposure_duration.seconds - 101) < 1e-3
    @@ -236,6 +269,7 @@ def test_make_capture_params():
         assert abs(cap_proto.exposure_duration.seconds - 101) < 1e-3
         assert abs(cap_proto.exposure_duration.nanos - int(.005 * 1e9)) <= 1
     
    +
     def test_visual_source_with_thread():
         barrier = threading.Barrier(2)
         inc = Increment(barrier)
    @@ -251,8 +285,11 @@ def test_visual_source_with_thread():
         finally:
             visual_src.stop_capturing()
     
    +
     def test_not_camera_interface():
    +
         class WrongFakeCamera():
    +
             def blocking_capture(self):
                 return 1
     
    @@ -264,6 +301,7 @@ def blocking_capture(self):
         # Check that instantiating a class with camera interface without the methods expected
         # will fail on creation.
         class BadFakeCamera(CameraInterface):
    +
             def missing_everything(self):
                 pass
     
    @@ -275,6 +313,7 @@ def missing_everything(self):
         # with pytest.raises(Exception):
         #     visual_src = VisualImageSource("source3", FakeCamera(capture_return_onething, decode_less_args))
     
    +
     def test_image_capture_thread():
         default_time = 1.1
         barrier = threading.Barrier(2)
    @@ -308,6 +347,7 @@ def test_image_capture_thread():
         with pytest.raises(threading.BrokenBarrierError):
             barrier.wait(0.5)
     
    +
     def _test_camera_service(use_background_capture_thread, logger=None):
         robot = MockRobot()
     
    @@ -315,19 +355,26 @@ def _test_camera_service(use_background_capture_thread, logger=None):
         r_amt = 10
         c_amt = 21
         gain = 25
    -    visual_src = VisualImageSource(src_name, FakeCamera(capture_fake, decode_fake), rows=r_amt, cols=c_amt, gain=gain)
    +    visual_src = VisualImageSource(src_name, FakeCamera(capture_fake, decode_fake), rows=r_amt,
    +                                   cols=c_amt, gain=gain)
         src_name2 = "source_cap_error"
    -    visual_src2 = VisualImageSource(src_name2, FakeCamera(capture_with_error, decode_fake), rows=r_amt, cols=c_amt)
    +    visual_src2 = VisualImageSource(src_name2, FakeCamera(capture_with_error, decode_fake),
    +                                    rows=r_amt, cols=c_amt)
         src_name3 = "source_decode_error"
    -    visual_src3 = VisualImageSource(src_name3, FakeCamera(capture_fake, decode_with_error), rows=r_amt, cols=c_amt)
    +    visual_src3 = VisualImageSource(src_name3, FakeCamera(capture_fake, decode_with_error),
    +                                    rows=r_amt, cols=c_amt)
         src_name4 = "source_capture_malformed"
    -    visual_src4 = VisualImageSource(src_name4, FakeCamera(capture_return_onething, decode_fake), rows=r_amt, cols=c_amt)
    +    visual_src4 = VisualImageSource(src_name4, FakeCamera(capture_return_onething, decode_fake),
    +                                    rows=r_amt, cols=c_amt)
         src_name5 = "source_decode_malformed"
    -    visual_src5 = VisualImageSource(src_name5, FakeCamera(capture_fake, decode_less_args), rows=r_amt, cols=c_amt)
    +    visual_src5 = VisualImageSource(src_name5, FakeCamera(capture_fake, decode_less_args),
    +                                    rows=r_amt, cols=c_amt)
         src_name6 = "source2"
    -    visual_src6 = VisualImageSource(src_name6, FakeCamera(capture_fake, decode_fake), rows=r_amt, cols=c_amt, gain=gain)
    +    visual_src6 = VisualImageSource(src_name6, FakeCamera(capture_fake, decode_fake), rows=r_amt,
    +                                    cols=c_amt, gain=gain)
         image_sources = [visual_src, visual_src2, visual_src3, visual_src4, visual_src5, visual_src6]
    -    camera_service = CameraBaseImageServicer(robot, "camera-service", image_sources,
    +    camera_service = CameraBaseImageServicer(
    +        robot, "camera-service", image_sources,
             use_background_capture_thread=use_background_capture_thread, logger=logger)
     
         req = image_pb2.ListImageSourcesRequest()
    @@ -352,7 +399,8 @@ def _test_camera_service(use_background_capture_thread, logger=None):
     
         # Request a known image source and make sure the response is as expected.
         req = image_pb2.GetImageRequest()
    -    req.image_requests.extend([image_pb2.ImageRequest(image_source_name=src_name, quality_percent=10)])
    +    req.image_requests.extend(
    +        [image_pb2.ImageRequest(image_source_name=src_name, quality_percent=10)])
         resp = camera_service.GetImage(req, None)
         assert resp.header.error.code == header_pb2.CommonError.CODE_OK
         assert len(resp.image_responses) == 1
    @@ -361,14 +409,17 @@ def _test_camera_service(use_background_capture_thread, logger=None):
         assert img_resp.source.rows == r_amt
         assert img_resp.source.cols == c_amt
         assert img_resp.shot.capture_params.gain == gain
    -    assert img_resp.shot.image.rows == 15 # Output of decode_fake
    +    assert img_resp.shot.image.rows == 15  # Output of decode_fake
         assert img_resp.shot.image.cols == c_amt
    -    assert abs(img_resp.shot.acquisition_time.seconds - 10) < 1e-3 # Robot converted timestamp
    -    assert abs(img_resp.shot.acquisition_time.nanos - 20) < 1e-3 # Robot converted timestamp
    +    assert abs(img_resp.shot.acquisition_time.seconds - 10) < 1e-3  # Robot converted timestamp
    +    assert abs(img_resp.shot.acquisition_time.nanos - 20) < 1e-3  # Robot converted timestamp
     
         # Request multiple image sources and make sure the response is complete.
         req = image_pb2.GetImageRequest()
    -    req.image_requests.extend([image_pb2.ImageRequest(image_source_name=src_name, quality_percent=10), image_pb2.ImageRequest(image_source_name=src_name6)])
    +    req.image_requests.extend([
    +        image_pb2.ImageRequest(image_source_name=src_name, quality_percent=10),
    +        image_pb2.ImageRequest(image_source_name=src_name6)
    +    ])
         resp = camera_service.GetImage(req, None)
         assert resp.header.error.code == header_pb2.CommonError.CODE_OK
         assert len(resp.image_responses) == 2
    @@ -382,7 +433,8 @@ def _test_camera_service(use_background_capture_thread, logger=None):
     
         # Request an image source that does not exist.
         req = image_pb2.GetImageRequest()
    -    req.image_requests.extend([image_pb2.ImageRequest(image_source_name="unknown", quality_percent=10)])
    +    req.image_requests.extend(
    +        [image_pb2.ImageRequest(image_source_name="unknown", quality_percent=10)])
         resp = camera_service.GetImage(req, None)
         assert resp.header.error.code == header_pb2.CommonError.CODE_OK
         assert len(resp.image_responses) == 1
    @@ -391,7 +443,8 @@ def _test_camera_service(use_background_capture_thread, logger=None):
     
         # Request an image from a source with a bad capture function.
         req = image_pb2.GetImageRequest()
    -    req.image_requests.extend([image_pb2.ImageRequest(image_source_name=src_name2, quality_percent=10)])
    +    req.image_requests.extend(
    +        [image_pb2.ImageRequest(image_source_name=src_name2, quality_percent=10)])
         resp = camera_service.GetImage(req, None)
         assert resp.header.error.code == header_pb2.CommonError.CODE_OK
         assert len(resp.image_responses) == 1
    @@ -400,7 +453,8 @@ def _test_camera_service(use_background_capture_thread, logger=None):
     
         # Request an image from a source with a decode error.
         req = image_pb2.GetImageRequest()
    -    req.image_requests.extend([image_pb2.ImageRequest(image_source_name=src_name3, quality_percent=10)])
    +    req.image_requests.extend(
    +        [image_pb2.ImageRequest(image_source_name=src_name3, quality_percent=10)])
         resp = camera_service.GetImage(req, None)
         assert resp.header.error.code == header_pb2.CommonError.CODE_OK
         assert len(resp.image_responses) == 1
    @@ -410,24 +464,30 @@ def _test_camera_service(use_background_capture_thread, logger=None):
     
         # Request an image with a malformed capture function (should raise an error so developer can fix).
         req = image_pb2.GetImageRequest()
    -    req.image_requests.extend([image_pb2.ImageRequest(image_source_name=src_name4, quality_percent=10)])
    +    req.image_requests.extend(
    +        [image_pb2.ImageRequest(image_source_name=src_name4, quality_percent=10)])
         resp = camera_service.GetImage(req, None)
         assert resp.header.error.code == header_pb2.CommonError.CODE_OK
         assert len(resp.image_responses) == 1
         img_resp = resp.image_responses[0]
         assert img_resp.status == image_pb2.ImageResponse.STATUS_IMAGE_DATA_ERROR
     
    +
     def test_image_service_no_thread():
         _test_camera_service(use_background_capture_thread=False)
     
    +
     def test_image_service_with_thread(caplog, capsys):
         # Disable the logger from printing messages because the background thread will repeatedly
         # print failure messages for all the "buggy" capture options that are being used to test
         # the service.
         _test_camera_service(use_background_capture_thread=True)
     
    +
     def test_gain_and_exposure_as_functions():
    +
         class GainAndExposure():
    +
             def __init__(self):
                 self.gain = 0
                 self.exposure = 0
    @@ -443,7 +503,8 @@ def get_exposure(self):
         # Check that gain/exposure functions are accepted inputs and get recalled each time you get the
         # capture parameters proto.
         ge = GainAndExposure()
    -    visual_src = VisualImageSource("source1", FakeCamera(capture_fake, decode_fake), gain=ge.get_gain, exposure=ge.get_exposure)
    +    visual_src = VisualImageSource("source1", FakeCamera(capture_fake, decode_fake),
    +                                   gain=ge.get_gain, exposure=ge.get_exposure)
         capture_params = visual_src.get_image_capture_params()
         assert capture_params.gain == 1
         assert capture_params.exposure_duration.seconds == 1
    diff --git a/python/bosdyn-client/tests/test_lease.py b/python/bosdyn-client/tests/test_lease.py
    index 6c76ad8ad..6596d2ef2 100644
    --- a/python/bosdyn-client/tests/test_lease.py
    +++ b/python/bosdyn-client/tests/test_lease.py
    @@ -16,8 +16,10 @@
     from bosdyn.client.lease import LeaseWallet
     from bosdyn.client.lease import NoSuchLease
     from bosdyn.client.lease import LeaseNotOwnedByWallet
    +from bosdyn.client.lease import test_active_lease as active_lease_test
     from bosdyn.api import lease_pb2 as LeaseProto
     
    +
     LLAMA = 'llama'
     MESO = 'mesozoic'
     SEQ = [500, 20, 9000]
    @@ -25,6 +27,15 @@
     # Start of Lease object tests.
     
     
    +@pytest.fixture
    +def body_lease_proto():
    +    proto = LeaseProto.Lease()
    +    proto.resource = 'body'
    +    proto.sequence[:] = [1]
    +    proto.epoch = 'epoch'
    +    return proto
    +
    +
     def _check_lease(expected_resource, expected_epoch, expected_sequence, actual_lease):
         assert expected_resource == actual_lease.lease_proto.resource
         assert expected_epoch == actual_lease.lease_proto.epoch
    @@ -75,11 +86,25 @@ def test_good_constructor():
     
     
     def test_compare_different_resource():
    +    client_name = 'testname'
         lease_a = _create_lease(LLAMA, MESO, SEQ)
         lease_b = _create_lease('koala', MESO, SEQ)
         assert Lease.CompareResult.DIFFERENT_RESOURCES == lease_a.compare(lease_b)
         assert Lease.CompareResult.DIFFERENT_RESOURCES == lease_b.compare(lease_a)
     
    +    # Test comparison with function that returns a lease use result and sublease of the incoming lease.
    +    lease_use_result, incoming_lease_subleased = active_lease_test(lease_a.lease_proto, lease_b,
    +                                                                   client_name)
    +    assert lease_use_result.status == LeaseProto.LeaseUseResult.STATUS_UNMANAGED
    +    assert lease_use_result.attempted_lease.sequence == lease_a.lease_proto.sequence
    +    # latest known lease is the "active lease":lease_b since incoming lease failed checks.
    +    assert lease_use_result.latest_known_lease.sequence == lease_b.lease_proto.sequence
    +    assert len(incoming_lease_subleased.lease_proto.sequence) > len(
    +        lease_a.lease_proto.sequence)  # subleases
    +    assert incoming_lease_subleased.lease_proto.resource == lease_a.lease_proto.resource
    +    assert incoming_lease_subleased.lease_proto.epoch == lease_a.lease_proto.epoch
    +    assert incoming_lease_subleased.lease_proto.client_names[-1] == client_name
    +
     
     def test_compare_different_epoch():
         lease_a = _create_lease(LLAMA, MESO, SEQ)
    @@ -87,6 +112,17 @@ def test_compare_different_epoch():
         assert Lease.CompareResult.DIFFERENT_EPOCHS == lease_a.compare(lease_b)
         assert Lease.CompareResult.DIFFERENT_EPOCHS == lease_b.compare(lease_a)
     
    +    # Test comparison with function that returns a lease use result and sublease of the incoming lease.
    +    lease_use_result, incoming_lease_subleased = active_lease_test(lease_a.lease_proto, lease_b, '')
    +    assert lease_use_result.status == LeaseProto.LeaseUseResult.STATUS_WRONG_EPOCH
    +    assert lease_use_result.attempted_lease.sequence == lease_a.lease_proto.sequence
    +    # latest known lease is the "active lease":lease_b since incoming lease failed checks.
    +    assert lease_use_result.latest_known_lease.sequence == lease_b.lease_proto.sequence
    +    assert len(incoming_lease_subleased.lease_proto.sequence) > len(
    +        lease_a.lease_proto.sequence)  # subleases
    +    assert incoming_lease_subleased.lease_proto.resource == lease_a.lease_proto.resource
    +    assert incoming_lease_subleased.lease_proto.epoch == lease_a.lease_proto.epoch
    +
     
     def test_compare_same():
         lease_a = _create_lease(LLAMA, MESO, SEQ)
    @@ -94,6 +130,17 @@ def test_compare_same():
         assert Lease.CompareResult.SAME == lease_a.compare(lease_b)
         assert Lease.CompareResult.SAME == lease_b.compare(lease_a)
     
    +    # Test comparison with function that returns a lease use result and sublease of the incoming lease.
    +    lease_use_result, incoming_lease_subleased = active_lease_test(lease_a.lease_proto, lease_b, '')
    +    assert lease_use_result.status == LeaseProto.LeaseUseResult.STATUS_OK
    +    assert lease_use_result.attempted_lease.sequence == lease_a.lease_proto.sequence
    +    # latest known lease is the sublease of lease_a, make sure the initial part of the sequence matches.
    +    assert lease_use_result.latest_known_lease.sequence[:-1] == lease_a.lease_proto.sequence
    +    assert len(incoming_lease_subleased.lease_proto.sequence) > len(
    +        lease_a.lease_proto.sequence)  # subleases
    +    assert incoming_lease_subleased.lease_proto.resource == lease_a.lease_proto.resource
    +    assert incoming_lease_subleased.lease_proto.epoch == lease_a.lease_proto.epoch
    +
     
     def test_compare_different_first_element():
         lease_a = _create_lease(LLAMA, MESO, SEQ)
    @@ -103,6 +150,27 @@ def test_compare_different_first_element():
         assert Lease.CompareResult.OLDER == lease_a.compare(lease_b)
         assert Lease.CompareResult.NEWER == lease_b.compare(lease_a)
     
    +    # Test comparison with function that returns a lease use result and sublease of the incoming lease.
    +    lease_use_result, incoming_lease_subleased = active_lease_test(lease_a.lease_proto, lease_b, '')
    +    assert lease_use_result.status == LeaseProto.LeaseUseResult.STATUS_OLDER
    +    assert lease_use_result.attempted_lease.sequence == lease_a.lease_proto.sequence
    +    # latest known lease is the "active lease":lease_b since incoming lease failed checks.
    +    assert lease_use_result.latest_known_lease.sequence == lease_b.lease_proto.sequence
    +    assert len(incoming_lease_subleased.lease_proto.sequence) > len(
    +        lease_a.lease_proto.sequence)  # subleases
    +    assert incoming_lease_subleased.lease_proto.resource == lease_a.lease_proto.resource
    +    assert incoming_lease_subleased.lease_proto.epoch == lease_a.lease_proto.epoch
    +
    +    lease_use_result, incoming_lease_subleased = active_lease_test(lease_b.lease_proto, lease_a, '')
    +    assert lease_use_result.status == LeaseProto.LeaseUseResult.STATUS_OK
    +    assert lease_use_result.attempted_lease.sequence == lease_b.lease_proto.sequence
    +    # latest known lease is the sublease of lease_b, make sure the initial part of the sequence matches.
    +    assert lease_use_result.latest_known_lease.sequence[:-1] == lease_b.lease_proto.sequence
    +    assert len(incoming_lease_subleased.lease_proto.sequence) > len(
    +        lease_b.lease_proto.sequence)  # subleases
    +    assert incoming_lease_subleased.lease_proto.resource == lease_b.lease_proto.resource
    +    assert incoming_lease_subleased.lease_proto.epoch == lease_b.lease_proto.epoch
    +
     
     def test_compare_different_second_element():
         lease_a = _create_lease(LLAMA, MESO, SEQ)
    @@ -130,6 +198,36 @@ def test_compare_manual_sub_lease():
         assert Lease.CompareResult.SUPER_LEASE == lease_a.compare(lease_b)
         assert Lease.CompareResult.SUB_LEASE == lease_b.compare(lease_a)
     
    +    # Test comparison with function that returns a lease use result and sublease of the incoming lease.
    +    # Note, by default the function does not allow super leases and will consider a super lease older.
    +    lease_use_result, incoming_lease_subleased = active_lease_test(lease_a.lease_proto, lease_b)
    +    assert lease_use_result.status == LeaseProto.LeaseUseResult.STATUS_OLDER
    +    assert lease_use_result.attempted_lease.sequence == lease_a.lease_proto.sequence
    +    assert lease_use_result.latest_known_lease.sequence == lease_b.lease_proto.sequence
    +    assert len(incoming_lease_subleased.lease_proto.sequence) == len(
    +        lease_a.lease_proto.sequence)  # didn't make a sublease.
    +    assert incoming_lease_subleased.lease_proto.resource == lease_a.lease_proto.resource
    +    assert incoming_lease_subleased.lease_proto.epoch == lease_a.lease_proto.epoch
    +
    +    lease_use_result, incoming_lease_subleased = active_lease_test(lease_a.lease_proto, lease_b,
    +                                                                   allow_super_leases=True)
    +    assert lease_use_result.status == LeaseProto.LeaseUseResult.STATUS_OK
    +    assert lease_use_result.attempted_lease.sequence == lease_a.lease_proto.sequence
    +    assert lease_use_result.latest_known_lease.sequence == lease_b.lease_proto.sequence
    +    assert len(incoming_lease_subleased.lease_proto.sequence) == len(
    +        lease_a.lease_proto.sequence)  # didn't make a sublease
    +    assert incoming_lease_subleased.lease_proto.resource == lease_a.lease_proto.resource
    +    assert incoming_lease_subleased.lease_proto.epoch == lease_a.lease_proto.epoch
    +
    +    lease_use_result, incoming_lease_subleased = active_lease_test(lease_b.lease_proto, lease_a, '')
    +    assert lease_use_result.status == LeaseProto.LeaseUseResult.STATUS_OK
    +    assert lease_use_result.attempted_lease.sequence == lease_b.lease_proto.sequence
    +    assert lease_use_result.latest_known_lease.sequence[:-1] == lease_b.lease_proto.sequence
    +    assert len(incoming_lease_subleased.lease_proto.sequence) > len(
    +        lease_b.lease_proto.sequence)  # subleases
    +    assert incoming_lease_subleased.lease_proto.resource == lease_b.lease_proto.resource
    +    assert incoming_lease_subleased.lease_proto.epoch == lease_b.lease_proto.epoch
    +
     
     def test_compare_auto_sub_lease():
         lease_a = _create_lease(LLAMA, MESO, SEQ)
    @@ -476,3 +574,28 @@ def test_lease_keep_alive_shutdown():
         # A second shutdown should also work, even if it is a no-op.
         keep_alive.shutdown()
         assert not keep_alive.is_alive()
    +
    +
    +def test_lease_compare_result_to_status():
    +    # Test the implicit conversion between CompareResult enum and LeaseUseResult status enum.
    +    assert Lease.compare_result_to_lease_use_result_status(
    +        Lease.CompareResult.SAME, False) == LeaseProto.LeaseUseResult.STATUS_OK
    +    assert Lease.compare_result_to_lease_use_result_status(
    +        Lease.CompareResult.SUPER_LEASE, False) == LeaseProto.LeaseUseResult.STATUS_OLDER
    +    assert Lease.compare_result_to_lease_use_result_status(
    +        Lease.CompareResult.SUPER_LEASE, True) == LeaseProto.LeaseUseResult.STATUS_OK
    +    assert Lease.compare_result_to_lease_use_result_status(
    +        Lease.CompareResult.SUB_LEASE, False) == LeaseProto.LeaseUseResult.STATUS_OK
    +    assert Lease.compare_result_to_lease_use_result_status(
    +        Lease.CompareResult.NEWER, False) == LeaseProto.LeaseUseResult.STATUS_OK
    +    assert Lease.compare_result_to_lease_use_result_status(
    +        Lease.CompareResult.OLDER, False) == LeaseProto.LeaseUseResult.STATUS_OLDER
    +    assert Lease.compare_result_to_lease_use_result_status(
    +        Lease.CompareResult.DIFFERENT_RESOURCES,
    +        False) == LeaseProto.LeaseUseResult.STATUS_UNMANAGED
    +    assert Lease.compare_result_to_lease_use_result_status(
    +        Lease.CompareResult.DIFFERENT_EPOCHS, False) == LeaseProto.LeaseUseResult.STATUS_WRONG_EPOCH
    +
    +    with pytest.raises(Exception):
    +        Lease.compare_result_to_lease_use_result_status(100, False)
    +
    diff --git a/python/bosdyn-client/tests/test_log_annotation.py b/python/bosdyn-client/tests/test_log_annotation.py
    index 132084f2d..d845ee63f 100644
    --- a/python/bosdyn-client/tests/test_log_annotation.py
    +++ b/python/bosdyn-client/tests/test_log_annotation.py
    @@ -24,6 +24,7 @@
         import mock
     
     
    +
     class DummyClass0:
     
         def __init__(self):
    diff --git a/python/bosdyn-client/tests/test_map_processing_client.py b/python/bosdyn-client/tests/test_map_processing_client.py
    new file mode 100644
    index 000000000..69359d437
    --- /dev/null
    +++ b/python/bosdyn-client/tests/test_map_processing_client.py
    @@ -0,0 +1,108 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""Unit tests for the map prcoessing module."""
    +import concurrent
    +import grpc
    +import pytest
    +
    +from bosdyn.api import header_pb2, lease_pb2, time_sync_pb2
    +from bosdyn.api.graph_nav import map_pb2
    +from bosdyn.api.graph_nav import map_processing_pb2, map_processing_service_pb2_grpc
    +from bosdyn.client.map_processing import MapProcessingServiceClient
    +from bosdyn.client.map_processing import __ANCHORING_COMMON_ERRORS
    +import bosdyn.client.map_processing
    +from bosdyn.client.exceptions import UnsetStatusError, InternalServerError
    +from bosdyn.client.time_sync import TimeSyncEndpoint
    +
    +
    +class MockMapProcessingServicer(map_processing_service_pb2_grpc.MapProcessingServiceServicer):
    +    """MapProcessing servicer for testing.
    +
    +    Provides simple, controllable implementations of certain RPCs.
    +    """
    +
    +    def __init__(self):
    +        super(MockMapProcessingServicer, self).__init__()
    +        self.common_header_code = header_pb2.CommonError.CODE_OK
    +        self.topology_process_status = map_processing_pb2.ProcessTopologyResponse.STATUS_OK
    +        self.anchoring_process_status = map_processing_pb2.ProcessAnchoringResponse.STATUS_OK
    +        self.graph = map_pb2.Graph()
    +        ed = self.graph.edges.add()
    +        ed.id.from_waypoint = 'w1'
    +        ed.id.to_waypoint = 'w2'
    +
    +    def ProcessTopology(self, request, context):
    +        """Fake ProcessTopology responses."""
    +        resp = map_processing_pb2.ProcessTopologyResponse()
    +        resp.header.error.code = self.common_header_code
    +        resp.status = self.topology_process_status
    +        resp.new_subgraph.CopyFrom(self.graph)
    +        yield resp
    +
    +    def ProcessAnchoring(self, request, context):
    +        """Fake ProcessAnchoring responses."""
    +        resp = map_processing_pb2.ProcessAnchoringResponse()
    +        resp.header.error.code = self.common_header_code
    +        resp.status = self.anchoring_process_status
    +        yield resp
    +
    +
    +@pytest.fixture
    +def client(time_sync):
    +    c = MapProcessingServiceClient()
    +    c._timesync_endpoint = time_sync
    +    return c
    +
    +
    +@pytest.fixture
    +def service():
    +    return MockMapProcessingServicer()
    +
    +
    +@pytest.fixture
    +def time_sync():
    +    ts = TimeSyncEndpoint(None)
    +    ts._locked_previous_response = time_sync_pb2.TimeSyncUpdateResponse()
    +    ts.response.state.status = time_sync_pb2.TimeSyncState.STATUS_OK
    +    return ts
    +
    +
    +@pytest.fixture
    +def server(client, service):
    +    server = grpc.server(concurrent.futures.ThreadPoolExecutor(max_workers=1))
    +    map_processing_service_pb2_grpc.add_MapProcessingServiceServicer_to_server(service, server)
    +    port = server.add_insecure_port('localhost:0')
    +    channel = grpc.insecure_channel('localhost:{}'.format(port))
    +    client.channel = channel
    +    server.start()
    +    yield server
    +    server.stop(0)
    +
    +
    +def test_processs_topology_exceptions(client, service, server):
    +    make_call = lambda: client.process_topology(map_processing_pb2.ProcessTopologyRequest.Params(),
    +                                                True)
    +    make_call()
    +
    +    service.topology_process_status = map_processing_pb2.ProcessTopologyResponse.STATUS_INVALID_GRAPH
    +    with pytest.raises(bosdyn.client.map_processing.InvalidGraphError):
    +        make_call()
    +
    +    service.topology_process_status = map_processing_pb2.ProcessTopologyResponse.STATUS_MISSING_WAYPOINT_SNAPSHOTS
    +    with pytest.raises(bosdyn.client.map_processing.MissingSnapshotsError):
    +        make_call()
    +
    +
    +def test_process_anchoring_exceptions(client, service, server):
    +    make_call = lambda: client.process_anchoring(
    +        map_processing_pb2.ProcessAnchoringRequest.Params(), True, False)
    +    make_call()
    +
    +    for (status, error) in __ANCHORING_COMMON_ERRORS.items():
    +        service.anchoring_process_status = status
    +        with pytest.raises(error):
    +            make_call()
    diff --git a/python/bosdyn-client/tests/test_math_helpers.py b/python/bosdyn-client/tests/test_math_helpers.py
    index 3a6d7844a..57af38743 100644
    --- a/python/bosdyn-client/tests/test_math_helpers.py
    +++ b/python/bosdyn-client/tests/test_math_helpers.py
    @@ -17,9 +17,10 @@
     # The following set of tests are for the math_helpers
     # Still needing tests: Quat class, se3_times_vec3
     
    +
     def test_create_se2_pose():
         # Test creating an SE2Pose from a proto with from_obj()
    -    proto_se2 = geometry_pb2.SE2Pose(position=geometry_pb2.Vec2(x=1,y=2), angle=.2)
    +    proto_se2 = geometry_pb2.SE2Pose(position=geometry_pb2.Vec2(x=1, y=2), angle=.2)
         se2 = SE2Pose.from_obj(proto_se2)
         assert type(se2) == SE2Pose
         assert se2.x == proto_se2.position.x
    @@ -46,59 +47,70 @@ def test_create_se2_pose():
         assert se2.y == proto_mut_se2.position.y
         assert se2.angle == proto_mut_se2.angle
     
    +
     def compare_math_helpers_se2(calculated_se2, expected_se2):
         assert fabs(calculated_se2.x - expected_se2.x) < 1e-5
         assert fabs(calculated_se2.y - expected_se2.y) < 1e-5
         assert fabs(calculated_se2.angle - expected_se2.angle) < 1e-5
     
    +
     def test_se2_times_se2():
         # Multiply all-zeros
         a = SE2Pose(x=0, y=0, angle=0)
         b = SE2Pose(x=0, y=0, angle=0)
    -    c = a * b #using the operator
    -    d = a.mult(b) #using the function
    +    c = a * b  #using the operator
    +    d = a.mult(b)  #using the function
         compare_math_helpers_se2(c, SE2Pose(x=0, y=0, angle=0))
         compare_math_helpers_se2(d, SE2Pose(x=0, y=0, angle=0))
     
         # A: No translation, only rotation. B: translation and rotation
         a = SE2Pose(x=0, y=0, angle=.2)
         b = SE2Pose(x=1, y=2, angle=1)
    -    c = a * b #using the operator
    -    d = a.mult(b) #using the function
    -    compare_math_helpers_se2(c, SE2Pose(x=1*cos(.2) + 2*-sin(.2), y=1*sin(.2) + 2*cos(.2), angle=1.2))
    -    compare_math_helpers_se2(d, SE2Pose(x=1*cos(.2) + 2*-sin(.2), y=1*sin(.2) + 2*cos(.2), angle=1.2))
    +    c = a * b  #using the operator
    +    d = a.mult(b)  #using the function
    +    compare_math_helpers_se2(
    +        c, SE2Pose(x=1 * cos(.2) + 2 * -sin(.2), y=1 * sin(.2) + 2 * cos(.2), angle=1.2))
    +    compare_math_helpers_se2(
    +        d, SE2Pose(x=1 * cos(.2) + 2 * -sin(.2), y=1 * sin(.2) + 2 * cos(.2), angle=1.2))
     
         # A: No rotation, only translation. B: translation and rotation
         a = SE2Pose(x=5, y=3.3, angle=0)
         b = SE2Pose(x=1, y=2, angle=1)
    -    c = a * b #using the operator
    -    d = a.mult(b) #using the function
    +    c = a * b  #using the operator
    +    d = a.mult(b)  #using the function
         compare_math_helpers_se2(c, SE2Pose(x=6, y=5.3, angle=1))
         compare_math_helpers_se2(d, SE2Pose(x=6, y=5.3, angle=1))
     
         # A: No rotation, only translation. B: No rotation, only translation.
         a = SE2Pose(x=5.2, y=3.3, angle=0)
         b = SE2Pose(x=1.3, y=2, angle=0)
    -    c = a * b #using the operator
    -    d = a.mult(b) #using the function
    +    c = a * b  #using the operator
    +    d = a.mult(b)  #using the function
         compare_math_helpers_se2(c, SE2Pose(x=6.5, y=5.3, angle=0))
         compare_math_helpers_se2(d, SE2Pose(x=6.5, y=5.3, angle=0))
     
         # A: Rotation and translation. B: Only rotation, no translation.
         a = SE2Pose(x=5.2, y=3.3, angle=.2)
         b = SE2Pose(x=0, y=0, angle=.3)
    -    c = a * b #using the operator
    -    d = a.mult(b) #using the function
    +    c = a * b  #using the operator
    +    d = a.mult(b)  #using the function
         compare_math_helpers_se2(c, SE2Pose(x=5.2, y=3.3, angle=.5))
         compare_math_helpers_se2(d, SE2Pose(x=5.2, y=3.3, angle=.5))
     
         # Both have rotation and translation
         a = SE2Pose(x=5.2, y=3.3, angle=.2)
         b = SE2Pose(x=1.2, y=2, angle=.3)
    -    c = a * b #using the operator
    -    d = a.mult(b) #using the function
    -    compare_math_helpers_se2(c, SE2Pose(x=5.2 + 1.2*cos(.2) + 2*-sin(.2), y=3.3 + 1.2*sin(.2) + 2*cos(.2), angle=.5))
    -    compare_math_helpers_se2(d, SE2Pose(x=5.2 + 1.2*cos(.2) + 2*-sin(.2), y=3.3 + 1.2*sin(.2) + 2*cos(.2), angle=.5))
    +    c = a * b  #using the operator
    +    d = a.mult(b)  #using the function
    +    compare_math_helpers_se2(
    +        c,
    +        SE2Pose(x=5.2 + 1.2 * cos(.2) + 2 * -sin(.2), y=3.3 + 1.2 * sin(.2) + 2 * cos(.2),
    +                angle=.5))
    +    compare_math_helpers_se2(
    +        d,
    +        SE2Pose(x=5.2 + 1.2 * cos(.2) + 2 * -sin(.2), y=3.3 + 1.2 * sin(.2) + 2 * cos(.2),
    +                angle=.5))
    +
     
     def test_inverse_se2():
         # Identity/all-zeros
    @@ -119,7 +131,9 @@ def test_inverse_se2():
         # Translation and rotation
         d = SE2Pose(x=1, y=2, angle=1)
         inv_d = d.inverse()
    -    compare_math_helpers_se2(inv_d, SE2Pose(x=-1*cos(1)-2*sin(1), y=1*sin(1)-2*cos(1), angle=-1))
    +    compare_math_helpers_se2(
    +        inv_d, SE2Pose(x=-1 * cos(1) - 2 * sin(1), y=1 * sin(1) - 2 * cos(1), angle=-1))
    +
     
     def test_matrices_se2():
         # Test making rotation matrix
    @@ -131,54 +145,57 @@ def test_matrices_se2():
         # Rotation se2 --> rotation matrix
         b = SE2Pose(x=1, y=2, angle=.5)
         rot_b = b.to_rot_matrix()
    -    assert rot_b.shape == (2,2)
    +    assert rot_b.shape == (2, 2)
         assert numpy.array_equal(rot_b, numpy.array([[cos(.5), -sin(.5)], [sin(.5), cos(.5)]]))
     
         # Test skew matrix
         # No translation se2 --> skew matrix
         d = SE2Pose(x=1, y=2, angle=.1)
         skew_d = skew_matrix_2d(d)
    -    assert skew_d.shape == (1,2)
    +    assert skew_d.shape == (1, 2)
         assert numpy.array_equal(skew_d, numpy.array([[d.y, -d.x]]))
     
         # Translation se2 --> skew matrix
         e = SE2Pose(x=1, y=2, angle=.1)
         skew_e = skew_matrix_2d(e)
    -    assert skew_e.shape == (1,2)
    +    assert skew_e.shape == (1, 2)
         assert numpy.array_equal(skew_e, numpy.array([[e.y, -e.x]]))
     
         # Test making adjoint matrices
         # All zeros --> adjoint
         c = SE2Pose(x=0, y=0, angle=0)
         adjoint_c = c.to_adjoint_matrix()
    -    assert adjoint_c.shape == (3,3)
    -    assert numpy.array_equal(adjoint_c, numpy.array([[cos(0), -sin(0), c.y],
    -                                    [sin(0), cos(0), -c.x],
    -                                    [0, 0, 1]]))
    +    assert adjoint_c.shape == (3, 3)
    +    assert numpy.array_equal(
    +        adjoint_c, numpy.array([[cos(0), -sin(0), c.y], [sin(0), cos(0), -c.x], [0, 0, 1]]))
     
         # No rotation se2 --> adjoint
         f = SE2Pose(x=1, y=2, angle=0)
         adjoint_f = f.to_adjoint_matrix()
    -    assert adjoint_f.shape == (3,3)
    -    assert numpy.array_equal(adjoint_f, numpy.array([[cos(f.angle), -sin(f.angle), f.y],
    -                                    [sin(f.angle), cos(f.angle), -f.x],
    -                                    [0, 0, 1]]))
    +    assert adjoint_f.shape == (3, 3)
    +    assert numpy.array_equal(
    +        adjoint_f,
    +        numpy.array([[cos(f.angle), -sin(f.angle), f.y], [sin(f.angle),
    +                                                          cos(f.angle), -f.x], [0, 0, 1]]))
     
         # No translation se2 --> adjoint
         f = SE2Pose(x=0, y=0, angle=.2)
         adjoint_f = f.to_adjoint_matrix()
    -    assert adjoint_f.shape == (3,3)
    -    assert numpy.array_equal(adjoint_f, numpy.array([[cos(f.angle), -sin(f.angle), f.y],
    -                                    [sin(f.angle), cos(f.angle), -f.x],
    -                                    [0, 0, 1]]))
    +    assert adjoint_f.shape == (3, 3)
    +    assert numpy.array_equal(
    +        adjoint_f,
    +        numpy.array([[cos(f.angle), -sin(f.angle), f.y], [sin(f.angle),
    +                                                          cos(f.angle), -f.x], [0, 0, 1]]))
     
         # Both translation and rotation se2 --> adjoint
         f = SE2Pose(x=2, y=5, angle=.2)
         adjoint_f = f.to_adjoint_matrix()
    -    assert adjoint_f.shape == (3,3)
    -    assert numpy.array_equal(adjoint_f, numpy.array([[cos(f.angle), -sin(f.angle), f.y],
    -                                    [sin(f.angle), cos(f.angle), -f.x],
    -                                    [0, 0, 1]]))
    +    assert adjoint_f.shape == (3, 3)
    +    assert numpy.array_equal(
    +        adjoint_f,
    +        numpy.array([[cos(f.angle), -sin(f.angle), f.y], [sin(f.angle),
    +                                                          cos(f.angle), -f.x], [0, 0, 1]]))
    +
     
     def test_se2_conversions_se3_pose():
         # Test converting se2pose --> se3pose with no height input
    @@ -242,9 +259,11 @@ def test_se2_conversions_se3_pose():
         assert se3_e.rot.y == 0
         assert fabs(se3_e.rot.z - 0.2474) < 1e-5
     
    +
     def test_create_se3_pose():
         # Test creating an SE3Pose from a proto with from_obj()
    -    proto_se3 = geometry_pb2.SE3Pose(position=geometry_pb2.Vec3(x=1,y=2,z=3), rotation=geometry_pb2.Quaternion(w=.1,x=.2,y=.2,z=.1))
    +    proto_se3 = geometry_pb2.SE3Pose(position=geometry_pb2.Vec3(x=1, y=2, z=3),
    +                                     rotation=geometry_pb2.Quaternion(w=.1, x=.2, y=.2, z=.1))
         se3 = SE3Pose.from_obj(proto_se3)
         assert type(se3) == SE3Pose
         assert se3.x == proto_se3.position.x
    @@ -300,6 +319,7 @@ def test_create_se3_pose():
         assert identity.rot.y == 0
         assert identity.rot.z == 0
     
    +
     def compare_math_helpers_se3(calculated_se3, expected_se3):
         assert fabs(calculated_se3.x - expected_se3.x) < 1e-5
         assert fabs(calculated_se3.y - expected_se3.y) < 1e-5
    @@ -309,30 +329,36 @@ def compare_math_helpers_se3(calculated_se3, expected_se3):
         assert fabs(calculated_se3.rot.y - expected_se3.rot.y) < 1e-5
         assert fabs(calculated_se3.rot.z - expected_se3.rot.z) < 1e-5
     
    +
     def test_se3_times_se3():
         # All zeros.
         a = SE3Pose(x=0, y=0, z=0, rot=Quat(w=1, x=0, y=0, z=0))
         b = SE3Pose(x=0, y=0, z=0, rot=Quat(w=1, x=0, y=0, z=0))
    -    c = a * b #using the operator
    -    d = a.mult(b) #using the function
    +    c = a * b  #using the operator
    +    d = a.mult(b)  #using the function
         compare_math_helpers_se3(c, SE3Pose(x=0, y=0, z=0, rot=Quat(w=1, x=0, y=0, z=0)))
         compare_math_helpers_se3(d, SE3Pose(x=0, y=0, z=0, rot=Quat(w=1, x=0, y=0, z=0)))
     
         # x+1 X yaw+90 -> x+1,yaw+90
         a = SE3Pose(x=1, y=0, z=0, rot=Quat(w=1, x=0, y=0, z=0))
         b = SE3Pose(x=0, y=0, z=0, rot=Quat(sqrt(2.0) / 2.0, 0, 0, sqrt(2.0) / 2.0))
    -    c = a * b #using the operator
    -    d = a.mult(b) #using the function
    -    compare_math_helpers_se3(c, SE3Pose(x=1, y=0, z=0, rot=Quat(w=sqrt(2.0) / 2.0, x=0, y=0, z=sqrt(2.0) / 2.0)))
    -    compare_math_helpers_se3(d, SE3Pose(x=1, y=0, z=0, rot=Quat(w=sqrt(2.0) / 2.0, x=0, y=0, z=sqrt(2.0) / 2.0)))
    +    c = a * b  #using the operator
    +    d = a.mult(b)  #using the function
    +    compare_math_helpers_se3(
    +        c, SE3Pose(x=1, y=0, z=0, rot=Quat(w=sqrt(2.0) / 2.0, x=0, y=0, z=sqrt(2.0) / 2.0)))
    +    compare_math_helpers_se3(
    +        d, SE3Pose(x=1, y=0, z=0, rot=Quat(w=sqrt(2.0) / 2.0, x=0, y=0, z=sqrt(2.0) / 2.0)))
     
         # yaw+90 X x+1 -> y+1,yaw+90
         a = SE3Pose(x=0, y=0, z=0, rot=Quat(sqrt(2.0) / 2.0, 0, 0, sqrt(2.0) / 2.0))
         b = SE3Pose(x=1, y=0, z=0, rot=Quat(w=1, x=0, y=0, z=0))
    -    c = a * b #using the operator
    -    d = a.mult(b) #using the function
    -    compare_math_helpers_se3(c, SE3Pose(x=0, y=1, z=0, rot=Quat(w=sqrt(2.0) / 2.0, x=0, y=0, z=sqrt(2.0) / 2.0)))
    -    compare_math_helpers_se3(d, SE3Pose(x=0, y=1, z=0, rot=Quat(w=sqrt(2.0) / 2.0, x=0, y=0, z=sqrt(2.0) / 2.0)))
    +    c = a * b  #using the operator
    +    d = a.mult(b)  #using the function
    +    compare_math_helpers_se3(
    +        c, SE3Pose(x=0, y=1, z=0, rot=Quat(w=sqrt(2.0) / 2.0, x=0, y=0, z=sqrt(2.0) / 2.0)))
    +    compare_math_helpers_se3(
    +        d, SE3Pose(x=0, y=1, z=0, rot=Quat(w=sqrt(2.0) / 2.0, x=0, y=0, z=sqrt(2.0) / 2.0)))
    +
     
     def test_se3_inverse():
         # Identity/all-zeros
    @@ -348,73 +374,70 @@ def test_se3_inverse():
         # Rotation only
         c = SE3Pose(x=0, y=0, z=0, rot=Quat(sqrt(2.0) / 2.0, 0, 0, sqrt(2.0) / 2.0))
         inv_c = c.inverse()
    -    compare_math_helpers_se3(inv_c, SE3Pose(x=0, y=0, z=0, rot=Quat(sqrt(2.0) / 2.0, 0, 0, -sqrt(2.0) / 2.0)))
    +    compare_math_helpers_se3(
    +        inv_c, SE3Pose(x=0, y=0, z=0, rot=Quat(sqrt(2.0) / 2.0, 0, 0, -sqrt(2.0) / 2.0)))
     
         # Translation and rotation
         d = SE3Pose(x=1, y=0, z=0, rot=Quat(sqrt(2.0) / 2.0, 0, 0, sqrt(2.0) / 2.0))
         inv_d = d.inverse()
    -    compare_math_helpers_se3(inv_d, SE3Pose(x=0, y=1, z=0, rot=Quat(sqrt(2.0) / 2.0, 0, 0, -sqrt(2.0) / 2.0)))
    +    compare_math_helpers_se3(
    +        inv_d, SE3Pose(x=0, y=1, z=0, rot=Quat(sqrt(2.0) / 2.0, 0, 0, -sqrt(2.0) / 2.0)))
    +
     
     def test_matrices_se3():
         # Test making skew matrix
         # No translation se2 --> rotation matrix
         a = SE3Pose(x=0, y=0, z=0, rot=Quat(w=1, x=0, y=0, z=0))
         skew_a = skew_matrix_3d(a)
    -    assert skew_a.shape == (3,3)
    -    assert numpy.array_equal(skew_a, numpy.zeros((3,3)))
    +    assert skew_a.shape == (3, 3)
    +    assert numpy.array_equal(skew_a, numpy.zeros((3, 3)))
     
         # Translation se2 --> skew matrix
         b = SE3Pose(x=1, y=2, z=3, rot=Quat(w=1, x=0, y=0, z=0))
         skew_b = skew_matrix_3d(b.position)
    -    assert skew_b.shape == (3,3)
    -    assert numpy.array_equal(skew_b, numpy.array([[0, -3, 2],
    -                                [3, 0, -1],
    -                                [-2, 1, 0]]))
    +    assert skew_b.shape == (3, 3)
    +    assert numpy.array_equal(skew_b, numpy.array([[0, -3, 2], [3, 0, -1], [-2, 1, 0]]))
     
         # Test making adjoint matrices
         # All zeros --> adjoint
         c = SE3Pose(x=0, y=0, z=0, rot=Quat(w=1, x=0, y=0, z=0))
         adjoint_c = c.to_adjoint_matrix()
    -    assert adjoint_c.shape == (6,6)
    +    assert adjoint_c.shape == (6, 6)
         assert numpy.array_equal(adjoint_c, numpy.identity(6))
     
         # No rotation se2 --> adjoint
         c = SE3Pose(x=1, y=2, z=3, rot=Quat(w=1, x=0, y=0, z=0))
         adjoint_c = c.to_adjoint_matrix()
    -    assert adjoint_c.shape == (6,6)
    -    assert numpy.array_equal(adjoint_c, numpy.array([[1,0,0,0,-3,2],
    -                                     [0,1,0,3,0,-1],
    -                                     [0,0,1,-2,1,0],
    -                                     [0,0,0,1,0,0],
    -                                     [0,0,0,0,1,0],
    -                                     [0,0,0,0,0,1]]))
    +    assert adjoint_c.shape == (6, 6)
    +    assert numpy.array_equal(
    +        adjoint_c,
    +        numpy.array([[1, 0, 0, 0, -3, 2], [0, 1, 0, 3, 0, -1], [0, 0, 1, -2, 1, 0],
    +                     [0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1]]))
     
         # No translation se2 --> adjoint
         c = SE3Pose(x=0, y=0, z=0, rot=Quat(w=.1, x=.2, y=.3, z=.4))
         adjoint_c = c.to_adjoint_matrix()
    -    assert adjoint_c.shape == (6,6)
    +    assert adjoint_c.shape == (6, 6)
         print(adjoint_c)
    -    assert numpy.allclose(adjoint_c, numpy.array([[.5, .04, .22, 0,0,0],
    -                                     [.2, .6, .2, 0,0,0],
    -                                     [.1, .28, .74, 0,0,0],
    -                                     [0,0,0,.5, .04, .22],
    -                                     [0,0,0, .2, .6, .2],
    -                                     [0,0,0, .1, .28, .74]]))
    +    assert numpy.allclose(
    +        adjoint_c,
    +        numpy.array([[.5, .04, .22, 0, 0, 0], [.2, .6, .2, 0, 0, 0], [.1, .28, .74, 0, 0, 0],
    +                     [0, 0, 0, .5, .04, .22], [0, 0, 0, .2, .6, .2], [0, 0, 0, .1, .28, .74]]))
     
         # Both translation and rotation se2 --> adjoint
         c = SE3Pose(x=1, y=2, z=3, rot=Quat(w=.1, x=.2, y=.3, z=.4))
         adjoint_c = c.to_adjoint_matrix()
    -    assert adjoint_c.shape == (6,6)
    -    assert numpy.allclose(adjoint_c, numpy.array([[.5, .04, .22, -.4, -1.24, .88],
    -                                     [.2, .6, .2, 1.4, -.16, -.08],
    -                                     [.1, .28, .74, -.8, .52, -.24],
    -                                     [0,0,0,.5, .04, .22],
    -                                     [0,0,0, .2, .6, .2],
    -                                     [0,0,0, .1, .28, .74]]))
    +    assert adjoint_c.shape == (6, 6)
    +    assert numpy.allclose(
    +        adjoint_c,
    +        numpy.array([[.5, .04, .22, -.4, -1.24, .88], [.2, .6, .2, 1.4, -.16, -.08],
    +                     [.1, .28, .74, -.8, .52, -.24], [0, 0, 0, .5, .04, .22], [0, 0, 0, .2, .6, .2],
    +                     [0, 0, 0, .1, .28, .74]]))
    +
     
     def test_create_se2_vel():
         # Test creating an SE2Velocity from a proto with from_obj()
    -    proto_se2 = geometry_pb2.SE2Velocity(linear=geometry_pb2.Vec2(x=1,y=2), angular=.2)
    +    proto_se2 = geometry_pb2.SE2Velocity(linear=geometry_pb2.Vec2(x=1, y=2), angular=.2)
         se2 = SE2Velocity.from_obj(proto_se2)
         assert type(se2) == SE2Velocity
         assert se2.linear_velocity_x == proto_se2.linear.x
    @@ -451,7 +474,7 @@ def test_create_se2_vel():
         assert vec[2] == proto_se2.angular
     
         # Test creating the SE2Velocity from a array
    -    vel_arr = numpy.array([1,2,3]).reshape((3,1))
    +    vel_arr = numpy.array([1, 2, 3]).reshape((3, 1))
         se2 = SE2Velocity.from_vector(vel_arr)
         assert type(se2) == SE2Velocity
         assert se2.linear_velocity_x == 1
    @@ -459,7 +482,7 @@ def test_create_se2_vel():
         assert se2.angular_velocity == 3
     
         # Test creating the SE2Velocity from a list
    -    vel_list = [1,2,3]
    +    vel_list = [1, 2, 3]
         se2 = SE2Velocity.from_vector(vel_list)
         assert type(se2) == SE2Velocity
         assert se2.linear_velocity_x == 1
    @@ -469,7 +492,8 @@ def test_create_se2_vel():
     
     def test_create_se3_vel():
         # Test creating an SE3Velocity from a proto with from_obj()
    -    proto_se3 = geometry_pb2.SE3Velocity(linear=geometry_pb2.Vec3(x=1,y=2,z=3), angular=geometry_pb2.Vec3(x=1,y=2,z=3))
    +    proto_se3 = geometry_pb2.SE3Velocity(linear=geometry_pb2.Vec3(x=1, y=2, z=3),
    +                                         angular=geometry_pb2.Vec3(x=1, y=2, z=3))
         se3 = SE3Velocity.from_obj(proto_se3)
         assert type(se3) == SE3Velocity
         assert se3.linear_velocity_x == proto_se3.linear.x
    @@ -523,7 +547,7 @@ def test_create_se3_vel():
         assert vec[5] == proto_se3.angular.z
     
         # Test creating the SE3Velocity from a array
    -    vel_arr = numpy.array([1,2,3,4,5,6]).reshape((6,1))
    +    vel_arr = numpy.array([1, 2, 3, 4, 5, 6]).reshape((6, 1))
         se3 = SE3Velocity.from_vector(vel_arr)
         assert type(se3) == SE3Velocity
         assert se3.linear_velocity_x == 1
    @@ -533,9 +557,8 @@ def test_create_se3_vel():
         assert se3.angular_velocity_y == 5
         assert se3.angular_velocity_z == 6
     
    -
         # Test creating the SE2Velocity from a list
    -    vel_list = [1,2,3,4,5,6]
    +    vel_list = [1, 2, 3, 4, 5, 6]
         se3 = SE3Velocity.from_vector(vel_list)
         assert type(se3) == SE3Velocity
         assert se3.linear_velocity_x == 1
    @@ -545,11 +568,13 @@ def test_create_se3_vel():
         assert se3.angular_velocity_y == 5
         assert se3.angular_velocity_z == 6
     
    +
     def compare_se2_velocity(expected_vel, calculated_vel):
         assert fabs(expected_vel.linear_velocity_x - calculated_vel.linear_velocity_x) < 1e-6
         assert fabs(expected_vel.linear_velocity_y - calculated_vel.linear_velocity_y) < 1e-6
         assert fabs(expected_vel.angular_velocity - calculated_vel.angular_velocity) < 1e-6
     
    +
     def compare_se3_velocity(expected_vel, calculated_vel, threshold=1e-6):
         assert fabs(expected_vel.linear_velocity_x - calculated_vel.linear_velocity_x) < threshold
         assert fabs(expected_vel.linear_velocity_y - calculated_vel.linear_velocity_y) < threshold
    @@ -558,18 +583,19 @@ def compare_se3_velocity(expected_vel, calculated_vel, threshold=1e-6):
         assert fabs(expected_vel.angular_velocity_y - calculated_vel.angular_velocity_y) < threshold
         assert fabs(expected_vel.angular_velocity_z - calculated_vel.angular_velocity_z) < threshold
     
    +
     def test_transform_velocity():
         # Note this test assumes the adjoint matrix test passes.
         # Identity SE(2)
         a = SE2Pose(x=0, y=0, angle=0)
    -    vel_a = SE2Velocity(1,2,.2)
    +    vel_a = SE2Velocity(1, 2, .2)
         adjoint_a = a.to_adjoint_matrix()
         transformed_a = transform_se2velocity(adjoint_a, vel_a)
         compare_se2_velocity(vel_a, transformed_a)
     
         # Identity SE(3)
         b = SE3Pose(x=0, y=0, z=0, rot=Quat(w=1, x=0, y=0, z=0))
    -    vel_b = SE3Velocity(1,2,3,.1,.2,.3)
    +    vel_b = SE3Velocity(1, 2, 3, .1, .2, .3)
         adjoint_b = b.to_adjoint_matrix()
         print(type(vel_b))
         print(type(vel_b) == SE3Velocity)
    @@ -578,18 +604,23 @@ def test_transform_velocity():
         compare_se3_velocity(vel_b, transformed_b)
     
         # Full SE(2) transformation
    -    c = SE2Pose(2,3,pi)
    -    vel_c = SE2Velocity(1,1,2)
    +    c = SE2Pose(2, 3, pi)
    +    vel_c = SE2Velocity(1, 1, 2)
         adjoint_c = c.to_adjoint_matrix()
         transformed_c = transform_se2velocity(adjoint_c, vel_c)
         compare_se2_velocity(transformed_c, SE2Velocity(5, -5, 2))
     
         # Full SE(3) transformations
    -    d = SE3Pose(x=1, y=2, z=1, rot=Quat(.707,.707,0,0))
    -    vel_d = SE3Velocity(1,2,3,1,2,3)
    +    d = SE3Pose(x=1, y=2, z=1, rot=Quat(.707, .707, 0, 0))
    +    vel_d = SE3Velocity(1, 2, 3, 1, 2, 3)
         adjoint_d = d.to_adjoint_matrix()
         transformed_d = transform_se3velocity(adjoint_d, vel_d)
    -    compare_se3_velocity(transformed_d, SE3Velocity(1+1.99909*2+1.0003*3, 2*.000302+5*(-0.999698)+1+3*(-.000302), -0.999698+5*.000302+-2, 1, .000302*2-0.999698*3, 0.999698*2+.000302*3), 1e-4)
    +    compare_se3_velocity(
    +        transformed_d,
    +        SE3Velocity(1 + 1.99909 * 2 + 1.0003 * 3,
    +                    2 * .000302 + 5 * (-0.999698) + 1 + 3 * (-.000302),
    +                    -0.999698 + 5 * .000302 + -2, 1, .000302 * 2 - 0.999698 * 3,
    +                    0.999698 * 2 + .000302 * 3), 1e-4)
     
     
     def test_quat_to_euler():
    diff --git a/python/bosdyn-client/tests/test_plugin_service.py b/python/bosdyn-client/tests/test_plugin_service.py
    index 521e2e8ca..ddd0576c0 100644
    --- a/python/bosdyn-client/tests/test_plugin_service.py
    +++ b/python/bosdyn-client/tests/test_plugin_service.py
    @@ -22,6 +22,7 @@
                                                                DataAcquisitionStoreHelper)
     from .helpers import make_async
     
    +
     @pytest.fixture
     def daq_store_client():
         client = mock.Mock()
    @@ -42,6 +43,7 @@ def daq_store_failure_client():
         client.store_image_async.return_value = future
         return client
     
    +
     @pytest.fixture
     def daq_robot(daq_store_client):
         robot = mock.Mock()
    @@ -127,6 +129,7 @@ def test_wait_for_stores_complete_cancel(daq_store_client):
         with pytest.raises(RequestCancelledError):
             store_helper.wait_for_stores_complete()
     
    +
     def test_simple_plugin(daq_robot):
         """Test that a basic plugin that completes right away works."""
         service = DataAcquisitionPluginService(daq_robot, single_capability, success_plugin_impl)
    diff --git a/python/bosdyn-client/tests/test_power.py b/python/bosdyn-client/tests/test_power.py
    index 2bff46be7..1511094f3 100644
    --- a/python/bosdyn-client/tests/test_power.py
    +++ b/python/bosdyn-client/tests/test_power.py
    @@ -128,7 +128,7 @@ def robot_command(self, command, end_times_sec=None, lease=None, **kwargs):
     class MockRobotStateClient(object):
     
         def __init__(self):
    -        self.power_state = robot_state_pb2.PowerState.STATE_ON
    +        self.power_state = robot_state_pb2.PowerState.MOTOR_POWER_STATE_ON
             self.feedback_fn = None
             self.executor = futures.ThreadPoolExecutor(max_workers=2)
     
    @@ -147,7 +147,8 @@ def test_power_on_success():
         timeout = 1.0
         mock_client.feedback_fn = lambda: time.sleep(timeout / 2.0)
         mock_client.response = power_pb2.STATUS_SUCCESS
    -    power.power_on(mock_client, timeout_sec=timeout)
    +    power.power_on(mock_client, timeout_sec=timeout, update_frequency=100)
    +
     
     def test_power_on_failure():
         mock_client = MockPowerClient()
    @@ -155,7 +156,7 @@ def test_power_on_failure():
         mock_client.feedback_fn = lambda: time.sleep(timeout / 2.0)
         mock_client.response = power_pb2.STATUS_FAULTED
         with pytest.raises(power.FaultedError, match=r".* Cannot power on due to a fault.*"):
    -        power.power_on(mock_client, timeout_sec=timeout)
    +        power.power_on(mock_client, timeout_sec=timeout, update_frequency=100)
     
     
     @pytest.mark.parametrize('feedback_fn', [None, lambda: time.sleep(3.0)])
    @@ -165,7 +166,7 @@ def test_power_on_timeout(feedback_fn):
         start = time.time()
         timeout = 1.0
         with pytest.raises(power.CommandTimedOutError):
    -        power.power_on(mock_client, timeout_sec=timeout)
    +        power.power_on(mock_client, timeout_sec=timeout, update_frequency=100)
         dt = time.time() - start
         assert abs(dt - timeout) < 0.1
     
    @@ -175,7 +176,7 @@ def test_emergency_power_off_success():
         timeout = 1.0
         mock_client.feedback_fn = lambda: time.sleep(timeout / 2.0)
         mock_client.response = power_pb2.STATUS_SUCCESS
    -    power.power_off(mock_client, timeout_sec=timeout)
    +    power.power_off(mock_client, timeout_sec=timeout, update_frequency=100)
     
     
     @pytest.mark.parametrize('feedback_fn', [None, lambda: time.sleep(3.0)])
    @@ -185,7 +186,7 @@ def test_emergency_power_off_timeout(feedback_fn):
         start = time.time()
         timeout = 1.0
         with pytest.raises(power.CommandTimedOutError):
    -        power.power_off(mock_client, timeout_sec=timeout)
    +        power.power_off(mock_client, timeout_sec=timeout, update_frequency=100)
         dt = time.time() - start
         assert abs(dt - timeout) < 0.1
     
    @@ -193,11 +194,11 @@ def test_emergency_power_off_timeout(feedback_fn):
     def test_safe_power_off_success():
         mock_command_client = MockRobotCommandClient()
         mock_state_client = MockRobotStateClient()
    -    mock_state_client.power_state = robot_state_pb2.PowerState.STATE_OFF
    +    mock_state_client.power_state = robot_state_pb2.PowerState.MOTOR_POWER_STATE_OFF
         timeout = 1.0
         mock_command_client.feedback_fn = lambda: time.sleep(timeout / 2.0)
         mock_command_client.response = power_pb2.STATUS_SUCCESS
    -    power.safe_power_off(mock_command_client, mock_state_client, timeout)
    +    power.safe_power_off(mock_command_client, mock_state_client, timeout, update_frequency=100)
     
     
     @pytest.mark.parametrize('feedback_fn', [None, lambda: time.sleep(3.0)])
    @@ -208,6 +209,88 @@ def test_safe_power_off_timeout(feedback_fn):
         start = time.time()
         timeout = 1.0
         with pytest.raises(power.CommandTimedOutError):
    -        power.safe_power_off(mock_command_client, mock_state_client, timeout)
    +        power.safe_power_off(mock_command_client, mock_state_client, timeout, update_frequency=100)
    +    dt = time.time() - start
    +    assert abs(dt - timeout) < 0.1
    +
    +
    +def test_safe_power_off_motors_success():
    +    mock_command_client = MockRobotCommandClient()
    +    mock_state_client = MockRobotStateClient()
    +    mock_state_client.power_state = robot_state_pb2.PowerState.MOTOR_POWER_STATE_OFF
    +    timeout = 1.0
    +    mock_command_client.feedback_fn = lambda: time.sleep(timeout / 2.0)
    +    mock_command_client.response = power_pb2.STATUS_SUCCESS
    +    power.safe_power_off_motors(mock_command_client, mock_state_client, timeout,
    +                                update_frequency=100)
    +
    +
    +@pytest.mark.parametrize('feedback_fn', [None, lambda: time.sleep(3.0)])
    +def test_safe_power_off_motors_timeout(feedback_fn):
    +    mock_command_client = MockRobotCommandClient()
    +    mock_state_client = MockRobotStateClient()
    +    mock_state_client.feedback_fn = feedback_fn
    +    start = time.time()
    +    timeout = 1.0
    +    with pytest.raises(power.CommandTimedOutError):
    +        power.safe_power_off_motors(mock_command_client, mock_state_client, timeout,
    +                                    update_frequency=100)
    +    dt = time.time() - start
    +    assert abs(dt - timeout) < 0.1
    +
    +
    +def test_safe_power_off_robot_success():
    +    mock_command_client = MockRobotCommandClient()
    +    mock_state_client = MockRobotStateClient()
    +    mock_power_client = MockPowerClient()
    +    mock_state_client.power_state = robot_state_pb2.PowerState.MOTOR_POWER_STATE_OFF
    +    timeout = 1.0
    +    mock_command_client.feedback_fn = lambda: time.sleep(timeout / 4.0)
    +    mock_command_client.response = power_pb2.STATUS_SUCCESS
    +    mock_power_client.feedback_fn = lambda: time.sleep(timeout / 4.0)
    +    mock_power_client.response = power_pb2.STATUS_SUCCESS
    +    power.safe_power_off_robot(mock_command_client, mock_state_client, mock_power_client, 20,
    +                               update_frequency=100)
    +
    +
    +@pytest.mark.parametrize('feedback_fn', [None, lambda: time.sleep(3.0)])
    +def test_safe_power_off_robot_timeout(feedback_fn):
    +    mock_command_client = MockRobotCommandClient()
    +    mock_state_client = MockRobotStateClient()
    +    mock_power_client = MockPowerClient()
    +    mock_state_client.feedback_fn = feedback_fn
    +    timeout = 1.0
    +    start = time.time()
    +    with pytest.raises(power.CommandTimedOutError):
    +        power.safe_power_off_robot(mock_command_client, mock_state_client, mock_power_client,
    +                                   timeout, update_frequency=100)
    +    dt = time.time() - start
    +    assert abs(dt - timeout) < 0.1
    +
    +def test_safe_power_cycle_robot_success():
    +    mock_command_client = MockRobotCommandClient()
    +    mock_state_client = MockRobotStateClient()
    +    mock_power_client = MockPowerClient()
    +    mock_state_client.power_state = robot_state_pb2.PowerState.MOTOR_POWER_STATE_OFF
    +    timeout = 1.0
    +    mock_command_client.feedback_fn = lambda: time.sleep(timeout / 4.0)
    +    mock_command_client.response = power_pb2.STATUS_SUCCESS
    +    mock_power_client.feedback_fn = lambda: time.sleep(timeout / 4.0)
    +    mock_power_client.response = power_pb2.STATUS_SUCCESS
    +    power.safe_power_cycle_robot(mock_command_client, mock_state_client, mock_power_client, 20,
    +                               update_frequency=100)
    +
    +
    +@pytest.mark.parametrize('feedback_fn', [None, lambda: time.sleep(3.0)])
    +def test_safe_power_cycle_robot_timeout(feedback_fn):
    +    mock_command_client = MockRobotCommandClient()
    +    mock_state_client = MockRobotStateClient()
    +    mock_power_client = MockPowerClient()
    +    mock_state_client.feedback_fn = feedback_fn
    +    timeout = 1.0
    +    start = time.time()
    +    with pytest.raises(power.CommandTimedOutError):
    +        power.safe_power_cycle_robot(mock_command_client, mock_state_client, mock_power_client,
    +                                   timeout, update_frequency=100)
         dt = time.time() - start
         assert abs(dt - timeout) < 0.1
    diff --git a/python/bosdyn-client/tests/test_robot_command.py b/python/bosdyn-client/tests/test_robot_command.py
    index 31d6ca00a..df8c94086 100644
    --- a/python/bosdyn-client/tests/test_robot_command.py
    +++ b/python/bosdyn-client/tests/test_robot_command.py
    @@ -8,14 +8,16 @@
     import pytest
     
     from bosdyn.client.robot_command import (_robot_command_error, _robot_command_feedback_error,
    -                                         _clear_behavior_fault_error, RobotCommandBuilder, _edit_proto,
    -                                         EDIT_TREE_CONVERT_LOCAL_TIME_TO_ROBOT_TIME, END_TIME_EDIT_TREE)
    +                                         _clear_behavior_fault_error, RobotCommandBuilder,
    +                                         _edit_proto, EDIT_TREE_CONVERT_LOCAL_TIME_TO_ROBOT_TIME,
    +                                         END_TIME_EDIT_TREE)
     
     from bosdyn.api import robot_command_pb2, arm_command_pb2, synchronized_command_pb2, geometry_pb2
     from bosdyn.client.frame_helpers import BODY_FRAME_NAME, ODOM_FRAME_NAME
     from bosdyn.client import ResponseError, InternalServerError, LeaseUseError, UnsetStatusError
     from google.protobuf import timestamp_pb2
     
    +
     def test_robot_command_error():
         # Test unset header error
         response = robot_command_pb2.RobotCommandResponse()
    @@ -121,6 +123,7 @@ def _test_has_gripper(command):
         assert isinstance(command, synchronized_command_pb2.SynchronizedCommand.Request)
         assert command.HasField("gripper_command")
     
    +
     def test_stop_command():
         command = RobotCommandBuilder.stop_command()
         _test_has_full_body(command)
    @@ -428,7 +431,9 @@ def test_build_synchro_command():
         with pytest.raises(Exception):
             command = RobotCommandBuilder.build_synchro_command(arm_command, full_body_command)
     
    +
     def test_edit_timestamps():
    +
         def _set_new_time(key, proto):
             """If proto has a field named key, fill set it to end_time_secs as robot time. """
             if key not in proto.DESCRIPTOR.fields_by_name:
    @@ -438,8 +443,8 @@ def _set_new_time(key, proto):
     
         command = robot_command_pb2.RobotCommand()
         command.synchronized_command.arm_command.arm_cartesian_command.root_frame_name = "test"
    -    command.synchronized_command.arm_command.arm_cartesian_command.pose_trajectory_in_task.reference_time.seconds =  25
    -    command.synchronized_command.arm_command.arm_cartesian_command.wrench_trajectory_in_task.reference_time.seconds =  25
    +    command.synchronized_command.arm_command.arm_cartesian_command.pose_trajectory_in_task.reference_time.seconds = 25
    +    command.synchronized_command.arm_command.arm_cartesian_command.wrench_trajectory_in_task.reference_time.seconds = 25
         _edit_proto(command, EDIT_TREE_CONVERT_LOCAL_TIME_TO_ROBOT_TIME, _set_new_time)
         assert command.synchronized_command.arm_command.arm_cartesian_command.pose_trajectory_in_task.reference_time.seconds == 10
         assert command.synchronized_command.arm_command.arm_cartesian_command.wrench_trajectory_in_task.reference_time.seconds == 10
    @@ -447,15 +452,15 @@ def _set_new_time(key, proto):
     
         command = robot_command_pb2.RobotCommand()
         command.synchronized_command.arm_command.arm_joint_move_command.trajectory.maximum_velocity.value = 1
    -    command.synchronized_command.arm_command.arm_joint_move_command.trajectory.reference_time.seconds =  25
    +    command.synchronized_command.arm_command.arm_joint_move_command.trajectory.reference_time.seconds = 25
         _edit_proto(command, EDIT_TREE_CONVERT_LOCAL_TIME_TO_ROBOT_TIME, _set_new_time)
         assert command.synchronized_command.arm_command.arm_joint_move_command.trajectory.reference_time.seconds == 10
         assert command.synchronized_command.arm_command.arm_joint_move_command.trajectory.maximum_velocity.value == 1
     
         command = robot_command_pb2.RobotCommand()
         command.synchronized_command.arm_command.arm_gaze_command.frame1_name = "test"
    -    command.synchronized_command.arm_command.arm_gaze_command.target_trajectory_in_frame1.reference_time.seconds =  25
    -    command.synchronized_command.arm_command.arm_gaze_command.tool_trajectory_in_frame2.reference_time.seconds =  25
    +    command.synchronized_command.arm_command.arm_gaze_command.target_trajectory_in_frame1.reference_time.seconds = 25
    +    command.synchronized_command.arm_command.arm_gaze_command.tool_trajectory_in_frame2.reference_time.seconds = 25
         _edit_proto(command, EDIT_TREE_CONVERT_LOCAL_TIME_TO_ROBOT_TIME, _set_new_time)
         assert command.synchronized_command.arm_command.arm_gaze_command.target_trajectory_in_frame1.reference_time.seconds == 10
         assert command.synchronized_command.arm_command.arm_gaze_command.tool_trajectory_in_frame2.reference_time.seconds == 10
    @@ -488,7 +493,6 @@ def _set_new_time(key, proto):
         _edit_proto(command, END_TIME_EDIT_TREE, _set_new_time)
         assert command.synchronized_command.mobility_command.stance_request.end_time.seconds == 10
     
    -
         command = robot_command_pb2.RobotCommand()
         command.synchronized_command.arm_command.arm_velocity_command.end_time.seconds = 25
         _edit_proto(command, END_TIME_EDIT_TREE, _set_new_time)
    diff --git a/python/bosdyn-client/tests/test_sdk.py b/python/bosdyn-client/tests/test_sdk.py
    index fa55bfec2..ee390e14b 100644
    --- a/python/bosdyn-client/tests/test_sdk.py
    +++ b/python/bosdyn-client/tests/test_sdk.py
    @@ -108,9 +108,9 @@ def test_client_creation(self):
         def test_load_robot_cert(self):
             sdk = bosdyn.client.Sdk()
             sdk.load_robot_cert()
    -        self.assertEqual(sdk.cert,
    -                         pkg_resources.resource_stream('bosdyn.client.resources',
    -                                                       'robot.pem').read())
    +        self.assertEqual(
    +            sdk.cert,
    +            pkg_resources.resource_stream('bosdyn.client.resources', 'robot.pem').read())
             with self.assertRaises(IOError):
                 sdk.load_robot_cert('this-path-does-not-exist')
     
    diff --git a/python/bosdyn-client/tests/test_spot_cam_compositor.py b/python/bosdyn-client/tests/test_spot_cam_compositor.py
    index 9a42b0cd5..6877137d2 100644
    --- a/python/bosdyn-client/tests/test_spot_cam_compositor.py
    +++ b/python/bosdyn-client/tests/test_spot_cam_compositor.py
    @@ -16,7 +16,9 @@
     
     from . import helpers
     
    +
     class MockCompositorService(service_pb2_grpc.CompositorServiceServicer):
    +
         def __init__(self, rpc_delay=0):
             """Create mock that returns fake compositor queries."""
             super(MockCompositorService, self).__init__()
    @@ -54,51 +56,62 @@ def GetVisibleCameras(self, request, context):
             helpers.add_common_header(response, request)
             return response
     
    +
     def _setup(rpc_delay=0):
         client = bosdyn.client.spot_cam.compositor.CompositorClient()
         service = MockCompositorService(rpc_delay=rpc_delay)
    -    server = helpers.setup_client_and_service(client, service,
    -                                              service_pb2_grpc.add_CompositorServiceServicer_to_server)
    +    server = helpers.setup_client_and_service(
    +        client, service, service_pb2_grpc.add_CompositorServiceServicer_to_server)
         return client, service, server
     
    +
     def _mock_stream(name='good', x_offset=0, y_offset=0, width=2, height=1):
         stream = compositor_pb2.GetVisibleCamerasResponse.Stream()
    -    stream.window.CopyFrom(compositor_pb2.GetVisibleCamerasResponse.Stream.Window(xoffset=x_offset, yoffset=y_offset, width=width, height=height))
    +    stream.window.CopyFrom(
    +        compositor_pb2.GetVisibleCamerasResponse.Stream.Window(xoffset=x_offset, yoffset=y_offset,
    +                                                               width=width, height=height))
         stream.camera.CopyFrom(camera_pb2.Camera(name=name))
         return stream
     
    +
     def test_set_screen():
         client, service, server = _setup()
         result = client.set_screen('good')
         assert result == 'good'
     
    +
     def test_set_screen_async():
         client, service, server = _setup()
         result = client.set_screen_async('good').result()
         assert result == 'good'
     
    +
     def test_get_screen():
         client, service, server = _setup()
         result = client.get_screen()
         assert result == 'good'
     
    +
     def test_get_screen_async():
         client, service, server = _setup()
         result = client.get_screen_async().result()
         assert result == 'good'
     
    +
     def test_list_screens():
         client, service, server = _setup()
         result = client.list_screens()
         assert len(result) == 1
         assert result[0].name == 'good'
     
    +
     def test_list_screens_async():
         client, service, server = _setup()
         result = client.list_screens_async().result()
         assert len(result) == 1
         assert result[0].name == 'good'
     
    +
     def test_get_visible_cameras():
         client, service, server = _setup()
         result = client.get_visible_cameras()
    diff --git a/python/bosdyn-client/tests/test_spot_cam_health.py b/python/bosdyn-client/tests/test_spot_cam_health.py
    index 48be1334d..7434d4830 100644
    --- a/python/bosdyn-client/tests/test_spot_cam_health.py
    +++ b/python/bosdyn-client/tests/test_spot_cam_health.py
    @@ -16,7 +16,9 @@
     
     from . import helpers
     
    +
     class MockHealthService(service_pb2_grpc.HealthServiceServicer):
    +
         def __init__(self, rpc_delay=0):
             """Create mock that returns fake health status."""
             super(MockHealthService, self).__init__()
    @@ -33,7 +35,10 @@ def GetBITStatus(self, request, context):
             time.sleep(self._rpc_delay)
     
             response = health_pb2.GetBITStatusResponse()
    -        response.degradations.extend([health_pb2.GetBITStatusResponse.Degradation(type=health_pb2.GetBITStatusResponse.Degradation.STORAGE, description='cool')])
    +        response.degradations.extend([
    +            health_pb2.GetBITStatusResponse.Degradation(
    +                type=health_pb2.GetBITStatusResponse.Degradation.STORAGE, description='cool')
    +        ])
             helpers.add_common_header(response, request)
             return response
     
    @@ -45,6 +50,7 @@ def GetTemperature(self, request, context):
             helpers.add_common_header(response, request)
             return response
     
    +
     def _setup(rpc_delay=0):
         client = bosdyn.client.spot_cam.health.HealthClient()
         service = MockHealthService(rpc_delay=rpc_delay)
    @@ -52,31 +58,37 @@ def _setup(rpc_delay=0):
                                                   service_pb2_grpc.add_HealthServiceServicer_to_server)
         return client, service, server
     
    +
     def test_clear_bit_events():
         client, service, server = _setup()
         client.clear_bit_events()
     
    +
     def test_clear_bit_events_async():
         client, service, server = _setup()
         client.clear_bit_events_async().result()
     
    +
     def test_get_bit_status():
         client, service, server = _setup()
         events, degradations = client.get_bit_status()
         assert len(events) == 0
         assert len(degradations) == 1
     
    +
     def test_get_bit_status_async():
         client, service, server = _setup()
         events, degradations = client.get_bit_status_async().result()
         assert len(events) == 0
         assert len(degradations) == 1
     
    +
     def test_get_temperature():
         client, service, server = _setup()
         temp = client.get_temperature()
         assert len(temp) == 1
     
    +
     def test_get_temperature_async():
         client, service, server = _setup()
         temp = client.get_temperature()
    diff --git a/python/bosdyn-client/tests/test_spot_cam_media_log.py b/python/bosdyn-client/tests/test_spot_cam_media_log.py
    index 832a39bd6..b61eb504c 100644
    --- a/python/bosdyn-client/tests/test_spot_cam_media_log.py
    +++ b/python/bosdyn-client/tests/test_spot_cam_media_log.py
    @@ -102,6 +102,7 @@ def Tag(self, request, context):
             helpers.add_common_header(response, request)
             return response
     
    +
     def _setup(rpc_delay=0):
         client = bosdyn.client.spot_cam.media_log.MediaLogClient()
         service = MockMediaLogService(rpc_delay=rpc_delay)
    @@ -131,14 +132,17 @@ def test_delete_async():
         completed_lp = _create_fake_logpoint()
         client.delete_async(completed_lp).result()
     
    +
     def test_enable_debug():
         client, service, server = _setup()
         client.enable_debug()
     
    +
     def test_enable_debug_async():
         client, service, server = _setup()
         client.enable_debug_async().result()
     
    +
     def test_get_status():
         client, service, server = _setup()
         completed_lp = _create_fake_logpoint()
    @@ -196,6 +200,7 @@ def test_retrieve():
         assert lp.SerializeToString() == completed_lp.SerializeToString()
         assert len(image_binary) == 0
     
    +
     def test_retrieve_raw_data():
         client, service, server = _setup()
         completed_lp = _create_fake_logpoint()
    @@ -203,14 +208,17 @@ def test_retrieve_raw_data():
         assert lp.SerializeToString() == completed_lp.SerializeToString()
         assert len(image_binary) == 0
     
    +
     def test_set_passphrase():
         client, service, server = _setup()
         client.set_passphrase('good')
     
    +
     def test_set_passphrase_async():
         client, service, server = _setup()
         client.set_passphrase_async('good').result()
     
    +
     def test_store():
         client, service, server = _setup()
         camera = camera_pb2.Camera()
    @@ -232,11 +240,13 @@ def test_store_async():
         assert lp.type == logging_pb2.Logpoint.STILLIMAGE
         assert lp.tag == camera_tag
     
    +
     def test_tag():
         client, service, server = _setup()
         completed_lp = _create_fake_logpoint()
         client.tag(completed_lp)
     
    +
     def test_tag_async():
         client, service, server = _setup()
         completed_lp = _create_fake_logpoint()
    diff --git a/python/bosdyn-client/tests/test_spot_cam_network.py b/python/bosdyn-client/tests/test_spot_cam_network.py
    index 39eaa6ca5..398bc7dd9 100644
    --- a/python/bosdyn-client/tests/test_spot_cam_network.py
    +++ b/python/bosdyn-client/tests/test_spot_cam_network.py
    @@ -19,13 +19,17 @@
     import socket
     import struct
     
    +
     def ip2int(addr):
         return struct.unpack("!I", socket.inet_aton(addr))[0]
     
    +
     def int2ip(addr):
         return socket.inet_ntoa(struct.pack("!I", addr))
     
    +
     class MockNetworkService(service_pb2_grpc.NetworkServiceServicer):
    +
         def __init__(self, rpc_delay=0):
             """Create mock that returns fake network statuses."""
             super(MockNetworkService, self).__init__()
    @@ -47,6 +51,7 @@ def SetICEConfiguration(self, request, context):
             return response
     
     
    +
     def _setup(rpc_delay=0):
         client = bosdyn.client.spot_cam.network.NetworkClient()
         service = MockNetworkService(rpc_delay=rpc_delay)
    @@ -54,6 +59,7 @@ def _setup(rpc_delay=0):
                                                   service_pb2_grpc.add_NetworkServiceServicer_to_server)
         return client, service, server
     
    +
     def _fill_ice_server(server, src=None):
         if not src:
             src = _mock_ice_server()
    @@ -61,6 +67,7 @@ def _fill_ice_server(server, src=None):
         server.CopyFrom(src)
         return server
     
    +
     def _mock_ice_server(server_type=network_pb2.ICEServer.TURN, address='127.0.0.1', port=22):
         server = network_pb2.ICEServer()
         server.type = server_type
    @@ -68,6 +75,7 @@ def _mock_ice_server(server_type=network_pb2.ICEServer.TURN, address='127.0.0.1'
         server.port = port
         return server
     
    +
     def test_get_ice_configuration():
         client, service, server = _setup()
         ice = client.get_ice_configuration()
    @@ -77,6 +85,7 @@ def test_get_ice_configuration():
         assert ice[0].address == mock.address
         assert ice[0].port == mock.port
     
    +
     def test_get_ice_configuration_async():
         client, service, server = _setup()
         ice = client.get_ice_configuration_async().result()
    @@ -86,11 +95,14 @@ def test_get_ice_configuration_async():
         assert ice[0].address == mock.address
         assert ice[0].port == mock.port
     
    +
     def test_set_ice_configuration():
         client, service, server = _setup()
         ice = client.set_ice_configuration([_mock_ice_server()])
     
    +
     def test_set_ice_configuration_async():
         client, service, server = _setup()
         ice = client.set_ice_configuration_async([_mock_ice_server()]).result()
     
    +
    diff --git a/python/bosdyn-client/tests/test_spot_cam_power.py b/python/bosdyn-client/tests/test_spot_cam_power.py
    index 31d7819a0..b1ad959d6 100644
    --- a/python/bosdyn-client/tests/test_spot_cam_power.py
    +++ b/python/bosdyn-client/tests/test_spot_cam_power.py
    @@ -16,7 +16,9 @@
     
     from . import helpers
     
    +
     class MockPowerService(service_pb2_grpc.PowerServiceServicer):
    +
         def __init__(self, rpc_delay=0):
             """Create mock that returns fake power statuses."""
             super(MockPowerService, self).__init__()
    @@ -40,6 +42,7 @@ def SetPowerStatus(self, request, context):
             helpers.add_common_header(response, request)
             return response
     
    +
     def _setup(rpc_delay=0):
         client = bosdyn.client.spot_cam.power.PowerClient()
         service = MockPowerService(rpc_delay=rpc_delay)
    @@ -47,6 +50,7 @@ def _setup(rpc_delay=0):
                                                   service_pb2_grpc.add_PowerServiceServicer_to_server)
         return client, service, server
     
    +
     def test_get_power_status():
         client, service, server = _setup()
         ps = client.get_power_status()
    @@ -54,6 +58,7 @@ def test_get_power_status():
         assert ps.aux1.value
         assert not ps.aux2.value
     
    +
     def test_get_power_status_async():
         client, service, server = _setup()
         ps = client.get_power_status_async().result()
    @@ -61,6 +66,7 @@ def test_get_power_status_async():
         assert ps.aux1.value
         assert not ps.aux2.value
     
    +
     def test_set_power_status():
         client, service, server = _setup()
         ps = client.set_power_status(ptz=True, aux1=False)
    @@ -68,6 +74,7 @@ def test_set_power_status():
         assert not ps.aux1.value
         assert not ps.aux2.value
     
    +
     def test_set_power_status_async():
         client, service, server = _setup()
         ps = client.set_power_status_async(ptz=True, aux1=False).result()
    diff --git a/python/bosdyn-client/tests/test_spot_cam_streamquality.py b/python/bosdyn-client/tests/test_spot_cam_streamquality.py
    index 1f72b8825..971b8b412 100644
    --- a/python/bosdyn-client/tests/test_spot_cam_streamquality.py
    +++ b/python/bosdyn-client/tests/test_spot_cam_streamquality.py
    @@ -16,7 +16,9 @@
     
     from . import helpers
     
    +
     class MockStreamQualityService(service_pb2_grpc.StreamQualityServiceServicer):
    +
         def __init__(self, rpc_delay=0):
             """Create mock that returns fake stream parameters."""
             super(MockStreamQualityService, self).__init__()
    @@ -38,14 +40,17 @@ def SetStreamParams(self, request, context):
             helpers.add_common_header(response, request)
             return response
     
    +
     def _setup(rpc_delay=0):
         client = bosdyn.client.spot_cam.streamquality.StreamQualityClient()
         service = MockStreamQualityService(rpc_delay=rpc_delay)
    -    server = helpers.setup_client_and_service(client, service,
    -                                              service_pb2_grpc.add_StreamQualityServiceServicer_to_server)
    +    server = helpers.setup_client_and_service(
    +        client, service, service_pb2_grpc.add_StreamQualityServiceServicer_to_server)
         return client, service, server
     
    -def _mock_stream_params(target_bitrate=100, refresh_interval=10, idr_interval=1, awb_mode=streamquality_pb2.StreamParams.AUTO):
    +
    +def _mock_stream_params(target_bitrate=100, refresh_interval=10, idr_interval=1,
    +                        awb_mode=streamquality_pb2.StreamParams.AUTO):
         sp = streamquality_pb2.StreamParams()
         sp.targetbitrate.value = target_bitrate
         sp.refreshinterval.value = refresh_interval
    @@ -53,24 +58,30 @@ def _mock_stream_params(target_bitrate=100, refresh_interval=10, idr_interval=1,
         sp.awb.CopyFrom(streamquality_pb2.StreamParams.AwbMode(awb=awb_mode))
         return sp
     
    +
     def test_set_stream_params():
         client, service, server = _setup()
         mock = _mock_stream_params()
    -    result = client.set_stream_params(mock.targetbitrate.value, mock.refreshinterval.value, mock.idrinterval.value, mock.awb.awb)
    +    result = client.set_stream_params(mock.targetbitrate.value, mock.refreshinterval.value,
    +                                      mock.idrinterval.value, mock.awb.awb)
         assert result.SerializeToString() == mock.SerializeToString()
     
    +
     def test_set_stream_params_async():
         client, service, server = _setup()
         mock = _mock_stream_params()
    -    result = client.set_stream_params_async(mock.targetbitrate.value, mock.refreshinterval.value, mock.idrinterval.value, mock.awb.awb).result()
    +    result = client.set_stream_params_async(mock.targetbitrate.value, mock.refreshinterval.value,
    +                                            mock.idrinterval.value, mock.awb.awb).result()
         assert result.SerializeToString() == mock.SerializeToString()
     
    +
     def test_get_stream_params():
         client, service, server = _setup()
         mock = _mock_stream_params()
         result = client.get_stream_params()
         assert result.SerializeToString() == mock.SerializeToString()
     
    +
     def test_get_stream_params():
         client, service, server = _setup()
         mock = _mock_stream_params()
    diff --git a/python/bosdyn-client/tests/test_token_manager.py b/python/bosdyn-client/tests/test_token_manager.py
    index eb9bd63ee..4e33a1d63 100644
    --- a/python/bosdyn-client/tests/test_token_manager.py
    +++ b/python/bosdyn-client/tests/test_token_manager.py
    @@ -14,6 +14,7 @@
     from bosdyn.client.token_manager import TokenManager, WriteFailedError
     from bosdyn.client.util import cli_login_prompt, cli_auth
     
    +
     class MockRobot:
     
         def __init__(self, token=None):
    @@ -43,11 +44,14 @@ def test_token_refresh():
     
         tm.stop()
     
    +
     def test_token_refresh_rpc_error():
         robot = MockRobot(token='mock-token-default')
    +
         def fail_with_rpc(token):
             fail_with_rpc.count += 1
             raise RpcError("Fake Rpc Error")
    +
         fail_with_rpc.count = 0
         robot.authenticate_with_token = fail_with_rpc
         assert robot.user_token == 'mock-token-default'
    @@ -60,22 +64,28 @@ def fail_with_rpc(token):
     
     def test_token_refresh_token_error():
         robot = MockRobot(token='mock-token-default')
    +
         def fail_with_rpc(token):
             raise InvalidTokenError(None)
    +
         robot.authenticate_with_token = fail_with_rpc
         assert robot.user_token == 'mock-token-default'
         local = datetime.datetime.now() + datetime.timedelta(hours=-2)
         tm = TokenManager(robot, timestamp=local)
         time.sleep(0.1)
    -    assert tm.is_alive() # For now we keep the tokenmanager retrying, so if the user does re-auth it
    -                         # will start updating the new token.
    +    assert tm.is_alive(
    +    )  # For now we keep the tokenmanager retrying, so if the user does re-auth it
    +    # will start updating the new token.
    +
     
     def test_token_refresh_write_error():
         robot = MockRobot(token='mock-token-default')
         original_auth = robot.authenticate_with_token
    +
         def fail_write(token):
             original_auth(token)
             raise WriteFailedError("Fake write failure")
    +
         robot.authenticate_with_token = fail_write
         assert robot.user_token == 'mock-token-default'
         local = datetime.datetime.now() + datetime.timedelta(hours=-2)
    @@ -85,6 +95,7 @@ def fail_write(token):
         assert tm.is_alive()
         tm.stop()
     
    +
     def test_cli_login(monkeypatch):
         real_login = ('user', 'password')
         monkeypatch.setattr('six.moves.input', lambda _: real_login[0])
    diff --git a/python/bosdyn-client/tests/test_utils.py b/python/bosdyn-client/tests/test_utils.py
    index 325a5de2d..7814ea9d8 100644
    --- a/python/bosdyn-client/tests/test_utils.py
    +++ b/python/bosdyn-client/tests/test_utils.py
    @@ -4,17 +4,18 @@
     # is subject to the terms and conditions of the Boston Dynamics Software
     # Development Kit License (20191101-BDSDK-SL).
     
    -"""Unit tests for the token_manager module."""
    +"""Unit tests for the server_utils module."""
     import datetime
     import pytest
     import time
     from bosdyn.api import data_acquisition_store_pb2 as daq_store
     from bosdyn.api import local_grid_pb2 as grid
    -from bosdyn.client.util import strip_large_bytes_fields, populate_response_header
    +from bosdyn.client.server_util import strip_large_bytes_fields, populate_response_header
    +
     
     def test_strip_large_bytes():
         req = daq_store.StoreImageRequest()
    -    temp_data = bytes("mybytes",'utf-8')
    +    temp_data = bytes("mybytes", 'utf-8')
         req.image.image.data = temp_data
         req.image.image.cols = 21
         assert len(req.image.image.data) > 0
    @@ -26,13 +27,14 @@ def test_strip_large_bytes():
         assert len(req.image.image.data) == 0
         assert req.image.image.cols == 21
     
    +
     def test_strip_large_bytes_repeated_msg():
         req = grid.GetLocalGridsResponse()
         grid1 = grid.LocalGridResponse()
    -    grid1.local_grid.data = bytes("mybytes",'utf-8')
    +    grid1.local_grid.data = bytes("mybytes", 'utf-8')
         grid1.local_grid.frame_name_local_grid_data = "my_frame"
         grid2 = grid.LocalGridResponse()
    -    grid2.local_grid.data = bytes("mybytes",'utf-8')
    +    grid2.local_grid.data = bytes("mybytes", 'utf-8')
         grid2.local_grid.frame_name_local_grid_data = "my_frame"
         req.local_grid_responses.extend([grid1, grid2])
     
    @@ -47,10 +49,11 @@ def test_strip_large_bytes_repeated_msg():
             assert len(g.local_grid.data) == 0
             assert g.local_grid.frame_name_local_grid_data == "my_frame"
     
    +
     def test_stripped_headers():
         request = daq_store.StoreImageRequest()
         request.header.client_name = "my_client"
    -    request.image.image.data = bytes("mybytes",'utf-8')
    +    request.image.image.data = bytes("mybytes", 'utf-8')
         request.image.image.cols = 21
         response = daq_store.StoreImageResponse()
     
    @@ -62,4 +65,4 @@ def test_stripped_headers():
         assert req_unpacked.image.image.cols == 21
         assert len(req_unpacked.image.image.data) == 0
         # check that the original request is unchanged.
    -    assert len(request.image.image.data) > 0
    \ No newline at end of file
    +    assert len(request.image.image.data) > 0
    diff --git a/python/bosdyn-core/src/bosdyn/bddf/README.md b/python/bosdyn-core/src/bosdyn/bddf/README.md
    index 60ae4ea8e..81c033a4c 100644
    --- a/python/bosdyn-core/src/bosdyn/bddf/README.md
    +++ b/python/bosdyn-core/src/bosdyn/bddf/README.md
    @@ -23,6 +23,7 @@ BDDF code and interfaces for the Boston Dynamics robot API.
     * [GRPC Reader](grpc_reader)
     * [GRPC Service Reader](grpc_service_reader)
     * [GRPC Service Writer](grpc_service_writer)
    +* [Message Reader](message_reader)
     * [POD Series Reader](pod_series_reader)
     * [POD Series Writer](pod_series_writer)
     * [Protobuf Channel Reader](protobuf_channel_reader)
    diff --git a/python/bosdyn-core/src/bosdyn/bddf/__init__.py b/python/bosdyn-core/src/bosdyn/bddf/__init__.py
    index 747125323..0cfaad9e1 100644
    --- a/python/bosdyn-core/src/bosdyn/bddf/__init__.py
    +++ b/python/bosdyn-core/src/bosdyn/bddf/__init__.py
    @@ -24,6 +24,9 @@
     # Class for registering a series which stores GRPC request/response pairs.
     from .grpc_service_writer import GrpcServiceWriter
     
    +# A class for reading message data from a DataFile.
    +from .message_reader import MessageReader
    +
     # Class for reading a series of POD data from a DataFile.
     from .pod_series_reader import PodSeriesReader
     
    diff --git a/python/bosdyn-core/src/bosdyn/bddf/base_data_reader.py b/python/bosdyn-core/src/bosdyn/bddf/base_data_reader.py
    index 6ea0fa598..d1fb4b20b 100644
    --- a/python/bosdyn-core/src/bosdyn/bddf/base_data_reader.py
    +++ b/python/bosdyn-core/src/bosdyn/bddf/base_data_reader.py
    @@ -17,12 +17,20 @@
     class BaseDataReader:  # pylint: disable=too-many-instance-attributes
         """Shared parent class for DataReader and StreamedDataReader."""
     
    -    def __init__(self, outfile):
    +    def __init__(self, infile=None, filename=None):
             """
    +        At least one of the following arguments must be specified.
    +
             Args:
    -         outfile:      binary file-like object for reading (e.g., from open(fname, "rb")).
    +         infile:      binary file-like object for reading (e.g., from open(fname, "rb")).
    +         filename:    path of input file, if applicable.
             """
    -        self._file = outfile
    +        self._file = infile
    +        self._filename = filename
    +        if not self._file:
    +            if not self._filename:
    +                raise ValueError("One of infile or filename must be specified")
    +            self._file = open(self._filename, 'rb')
             self._file_descriptor = None
             self._spec_index = None
             self._index_offset = None
    @@ -32,6 +40,11 @@ def __init__(self, outfile):
             self._file_index = None
             self._read_header()
     
    +    @property
    +    def filename(self):
    +        """Return input file name, if specified, or None if not."""
    +        return self._filename
    +
         @property
         def file_descriptor(self):
             """Return the file descriptor from the start of the file/stream."""
    diff --git a/python/bosdyn-core/src/bosdyn/bddf/data_reader.py b/python/bosdyn-core/src/bosdyn/bddf/data_reader.py
    index 4e8dedfb4..a69a67ff2 100644
    --- a/python/bosdyn-core/src/bosdyn/bddf/data_reader.py
    +++ b/python/bosdyn-core/src/bosdyn/bddf/data_reader.py
    @@ -18,12 +18,15 @@ class DataReader(BaseDataReader):  # pylint: disable=too-many-instance-attribute
         Methods raise ParseError if there is a problem with the format of the file.
         """
     
    -    def __init__(self, outfile):
    +    def __init__(self, infile=None, filename=None):
             """
    +        At least one of the following arguments must be specified.
    +
             Args:
    -         outfile:      binary file-like object for reading (e.g., from open(fname, "rb")).
    +         infile:      binary file-like object for reading (e.g., from open(fname, "rb")).
    +         filename:    path of input file, if applicable.
             """
    -        super(DataReader, self).__init__(outfile)
    +        super(DataReader, self).__init__(infile, filename)
             self._series_index_to_descriptor = {}
             self._series_index_to_block_index = {}  # {series_index -> SeriesBlockIndex}
             self._read_index()
    diff --git a/python/bosdyn-core/src/bosdyn/bddf/data_writer.py b/python/bosdyn-core/src/bosdyn/bddf/data_writer.py
    index 9352c4c2c..6c68b564f 100644
    --- a/python/bosdyn-core/src/bosdyn/bddf/data_writer.py
    +++ b/python/bosdyn-core/src/bosdyn/bddf/data_writer.py
    @@ -8,8 +8,9 @@
     
     import bosdyn.api.bddf_pb2 as bddf
     
    -from . block_writer import BlockWriter
    -from . file_indexer import FileIndexer
    +from .block_writer import BlockWriter
    +from .file_indexer import FileIndexer
    +
     
     class DataWriter:
         """Class for writing data to a file."""
    diff --git a/python/bosdyn-core/src/bosdyn/bddf/grpc_reader.py b/python/bosdyn-core/src/bosdyn/bddf/grpc_reader.py
    index c4db06ce7..2d104530c 100644
    --- a/python/bosdyn-core/src/bosdyn/bddf/grpc_reader.py
    +++ b/python/bosdyn-core/src/bosdyn/bddf/grpc_reader.py
    @@ -22,8 +22,7 @@ def __init__(self, data_reader, protobuf_classes):
             self._service_name_to_reader = {}
             self._series_index_to_reader = {}
             proto_name_to_class = {
    -            proto_class.DESCRIPTOR.full_name: proto_class
    -            for proto_class in protobuf_classes
    +            proto_class.DESCRIPTOR.full_name: proto_class for proto_class in protobuf_classes
             }
             self._proto_name_to_reader = {}
             for series_index, series_identifier in enumerate(data_reader.file_index.series_identifiers):
    diff --git a/python/bosdyn-core/src/bosdyn/bddf/message_reader.py b/python/bosdyn-core/src/bosdyn/bddf/message_reader.py
    new file mode 100644
    index 000000000..b7f55ba99
    --- /dev/null
    +++ b/python/bosdyn-core/src/bosdyn/bddf/message_reader.py
    @@ -0,0 +1,91 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""A class for reading message data from a DataFile."""
    +
    +from .common import PROTOBUF_CONTENT_TYPE
    +
    +
    +class MessageReader:
    +    """A class for reading message data from a DataFile.
    +
    +    Methods raise ParseError if there is a problem with the format of the file.
    +    """
    +
    +    def __init__(self, data_reader, require_protobuf=False):
    +        self._data_reader = data_reader
    +        self._channel_name_to_series_descriptor = {}
    +        self._channel_name_to_series_index = {}
    +        for series_index, series_identifier in enumerate(data_reader.file_index.series_identifiers):
    +            try:
    +                channel_name = series_identifier.spec["bosdyn:channel"]
    +            except KeyError:
    +                continue  # Not a protobuf series
    +            series_descriptor = self._data_reader.series_descriptor(series_index)
    +            if series_descriptor.WhichOneof("DataType") != "message_type":
    +                continue  # Not a protobuf series
    +            message_type = series_descriptor.message_type
    +            if require_protobuf and message_type.content_type != PROTOBUF_CONTENT_TYPE:
    +                continue  # Not a protobuf series
    +            self._channel_name_to_series_descriptor[channel_name] = series_descriptor
    +            self._channel_name_to_series_index[channel_name] = series_descriptor.series_index
    +
    +    @property
    +    def data_reader(self):
    +        """Return underlying DataReader this object is using."""
    +        return self._data_reader
    +
    +    @property
    +    def channel_name_to_series_decriptor(self):
    +        """Return a mapping of {chanel name -> series descriptor} for message series."""
    +        return self._channel_name_to_series_descriptor
    +
    +    def series_index(self, channel_name, message_type=None):
    +        """Return series index (int) to access SeriesDescriptors and messages.
    +
    +        Args:
    +         channel_name: name of the channel of messages.
    +         message_type: specify message type, if channel may have multiple
    +                        kinds of messages (optional)
    +        """
    +        if message_type is None:
    +            return self._channel_name_to_series_index[channel_name]
    +
    +        for series_index, series_identifier in enumerate(
    +                self._data_reader.file_index.series_identifiers):
    +            try:
    +                series_channel = series_identifier.spec["bosdyn:channel"]
    +            except KeyError:
    +                continue  # Not a protobuf series
    +            if series_channel != channel_name:
    +                continue
    +            series_descriptor = self._data_reader.series_descriptor(series_index)
    +            if series_descriptor.WhichOneof("DataType") != "message_type":
    +                continue  # Not a protobuf series
    +            if message_type == series_descriptor.message_type.type_name:
    +                return series_index
    +
    +        raise KeyError("No series with channel_name={} and message_type={}".format(
    +            channel_name, message_type))
    +
    +    def series_index_to_descriptor(self, series_index):
    +        """Given a series index, return the associated SeriesDescriptor
    +
    +        Args:
    +         series_index:  index (int) from the series_index() call
    +        """
    +        return self._data_reader.file_index.series_descriptor(series_index)
    +
    +    def get_blob(self, series_index, index_in_series):
    +        """Return binary data from message stored in the file.
    +
    +        Args:
    +         series_index:     index (int) from the series_index() call
    +         index_in_series:  the index of the message within the series
    +
    +        Returns: DataTypeDescriptor for channel, timestamp_nsec (int), binary data
    +        """
    +        return self._data_reader.read(series_index, index_in_series)
    diff --git a/python/bosdyn-core/src/bosdyn/bddf/protobuf_channel_reader.py b/python/bosdyn-core/src/bosdyn/bddf/protobuf_channel_reader.py
    index cfc1674b2..24e4310db 100644
    --- a/python/bosdyn-core/src/bosdyn/bddf/protobuf_channel_reader.py
    +++ b/python/bosdyn-core/src/bosdyn/bddf/protobuf_channel_reader.py
    @@ -14,7 +14,8 @@ def __init__(self, protobuf_reader, protobuf_type, channel_name=None):
             self._protobuf_reader = protobuf_reader
             self._protobuf_type = protobuf_type
             self._channel_name = channel_name or protobuf_type.DESCRIPTOR.full_name
    -        self._series_index = self._protobuf_reader.series_index(self._channel_name)
    +        self._series_index = self._protobuf_reader.series_index(
    +            self._channel_name, message_type=protobuf_type.DESCRIPTOR.full_name)
             self._descriptor = None
             self._num_messages = None
     
    @@ -45,3 +46,22 @@ def get_message(self, index_in_series):
                                                                       self._protobuf_type,
                                                                       index_in_series)
             return timestamp, msg
    +
    +
    +    def __iter__(self):
    +        return ProtobufChannelReader.Iterator(self)
    +
    +    class Iterator:  # pylint: disable=too-few-public-methods
    +        """Iterator over messages from a ProtobufChannelReader"""
    +
    +        def __init__(self, channel_reader):
    +            self._channel_reader = channel_reader
    +            self._index = 0
    +
    +        def __next__(self):
    +            """Returns the next vlue."""
    +            if self._index >= self._channel_reader.num_messages:
    +                raise StopIteration
    +            msg = self._channel_reader.get_message(self._index)
    +            self._index += 1
    +            return msg
    diff --git a/python/bosdyn-core/src/bosdyn/bddf/protobuf_reader.py b/python/bosdyn-core/src/bosdyn/bddf/protobuf_reader.py
    index 0f2beb4ca..a91ba7e4b 100644
    --- a/python/bosdyn-core/src/bosdyn/bddf/protobuf_reader.py
    +++ b/python/bosdyn-core/src/bosdyn/bddf/protobuf_reader.py
    @@ -5,54 +5,17 @@
     # Development Kit License (20191101-BDSDK-SL).
     
     """A class for reading Protobuf data from a DataFile."""
    +from .message_reader import MessageReader
     
    -from .common import PROTOBUF_CONTENT_TYPE
     
    -
    -class ProtobufReader:
    +class ProtobufReader(MessageReader):
         """A class for reading Protobuf data from a DataFile.
     
         Methods raise ParseError if there is a problem with the format of the file.
         """
     
         def __init__(self, data_reader):
    -        self._data_reader = data_reader
    -        self._channel_name_to_series_descriptor = {}
    -        self._channel_name_to_series_index = {}
    -        for series_index, series_identifier in enumerate(data_reader.file_index.series_identifiers):
    -            try:
    -                channel_name = series_identifier.spec["bosdyn:channel"]
    -            except KeyError:
    -                continue  # Not a protobuf series
    -            series_descriptor = self._data_reader.series_descriptor(series_index)
    -            if series_descriptor.WhichOneof("DataType") != "message_type":
    -                continue  # Not a protobuf series
    -            message_type = series_descriptor.message_type
    -            if message_type.content_type != PROTOBUF_CONTENT_TYPE:
    -                continue  # Not a protobuf series
    -            self._channel_name_to_series_descriptor[channel_name] = series_descriptor
    -            self._channel_name_to_series_index[channel_name] = series_descriptor.series_index
    -
    -    @property
    -    def data_reader(self):
    -        """Return underlying DataReader this object is using."""
    -        return self._data_reader
    -
    -    def series_index(self, channel_name):
    -        """Return the series index (int) by which SeriesDescriptors and messages can be accessed.
    -
    -        Args:
    -         channel_name: name of the channel of messages.
    -        """
    -        return self._channel_name_to_series_index[channel_name]
    -
    -    def series_index_to_descriptor(self, series_index):
    -        """Given a series index, return the associated SeriesDescriptor
    -
    -        Args:
    -         series_index:  index (int) from the series_index() call
    -        """
    -        return self._data_reader.file_index.series_descriptor(series_index)
    +        super(ProtobufReader, self).__init__(data_reader, require_protobuf=True)
     
         def get_message(self, series_index, protobuf_type, index_in_series):
             """Return a deserialized protobuf from bytes stored in the file.
    @@ -64,7 +27,7 @@ def get_message(self, series_index, protobuf_type, index_in_series):
     
             Returns: DataTypeDescriptor for channel, timestamp_nsec (int), deserialized protobuf object
             """
    -        desc, timestamp_nsec, data = self._data_reader.read(series_index, index_in_series)
    +        desc, timestamp_nsec, data = self.get_blob(series_index, index_in_series)
             protobuf = protobuf_type()
             protobuf.ParseFromString(data)
             return desc, timestamp_nsec, protobuf
    diff --git a/python/bosdyn-core/src/bosdyn/bddf/protobuf_series_writer.py b/python/bosdyn-core/src/bosdyn/bddf/protobuf_series_writer.py
    index fb83172e2..22a14f8d7 100644
    --- a/python/bosdyn-core/src/bosdyn/bddf/protobuf_series_writer.py
    +++ b/python/bosdyn-core/src/bosdyn/bddf/protobuf_series_writer.py
    @@ -6,8 +6,9 @@
     
     """Class for registering a series which stores protobuf messages in a message series."""
     
    -from . bosdyn import MessageChannel
    -from . common import PROTOBUF_CONTENT_TYPE
    +from .bosdyn import MessageChannel
    +from .common import PROTOBUF_CONTENT_TYPE
    +
     
     class ProtobufSeriesWriter:  # pylint: disable=too-many-instance-attributes
         """A class for registering a series which stores protobuf messages in a message series.
    diff --git a/python/bosdyn-core/src/bosdyn/util.py b/python/bosdyn-core/src/bosdyn/util.py
    index 157359d69..e2521dd71 100644
    --- a/python/bosdyn-core/src/bosdyn/util.py
    +++ b/python/bosdyn-core/src/bosdyn/util.py
    @@ -6,13 +6,14 @@
     
     """Common utilities for API Python code."""
     from __future__ import division
    +
    +import datetime
     import re
     import sys
     import time
    -import datetime
     
    -from google.protobuf.timestamp_pb2 import Timestamp
     from google.protobuf.duration_pb2 import Duration
    +from google.protobuf.timestamp_pb2 import Timestamp
     
     if sys.version_info[0] >= 3:
         LONG = int
    @@ -104,11 +105,27 @@ def sec_to_nsec(secs):
         return LONG(secs * NSEC_PER_SEC)
     
     
    +def nsec_to_sec(secs):
    +    """Convert time in nanoseconds to a timestamp in seconds.
    +
    +    Args:
    +     secs: Time in nanoseconds
    +    Returns:
    +     The time in seconds, as a .
    +    """
    +    return float(secs) / NSEC_PER_SEC
    +
    +
     def now_nsec():
         """Returns nanoseconds from dawn of unix epoch until when this is called."""
         return sec_to_nsec(time.time())
     
     
    +def now_sec():
    +    """Returns seconds from dawn of unix epoch until when this is called."""
    +    return time.time()
    +
    +
     def set_timestamp_from_now(timestamp_proto):
         """Sets google.protobuf.Timestamp to point to the current time on the system clock.
     
    @@ -288,7 +305,16 @@ def parse_datetime(val):
     
     
     def parse_timespan(timespan_spec):
    -    """Parse the timespan."""
    +    """Parse a timespan spec of the form {from-time}[-{to-time}]
    +
    +    Args:
    +     val: string with format {spec} or {spec}-{spec} where {spec} is a string
    +             with a format as described by TIME_FORMAT_DESC.
    +
    +    Returns: (datetime.datetime, None) or (datetime.datetime, datetime.datetime).
    +
    +    Raises: DatetimeParseError if format of val is not recognized.
    +    """
         dash_idx = timespan_spec.find('-')
         if dash_idx < 0:
             return parse_datetime(timespan_spec), None
    @@ -336,3 +362,11 @@ def convert_timestamp_from_local_to_robot(self, timestamp_proto):
               timestamp_proto[in/out] (google.protobuf.Timestamp): local system time time
             """
             timestamp_proto.CopyFrom(self.robot_timestamp_from_local(timestamp_proto))
    +
    +    def robot_seconds_from_local_seconds(self, local_time_secs):
    +        """Returns the robot time in seconds from a local time in seconds.
    +
    +        Args:
    +          local_time_secs:  Local system time time, in seconds from the unix epoch.
    +        """
    +        return local_time_secs + nsec_to_sec(self._clock_skew_nsec)
    diff --git a/python/bosdyn-core/tests/test_bddf.py b/python/bosdyn-core/tests/test_bddf.py
    index 77d787b83..ad269afd8 100644
    --- a/python/bosdyn-core/tests/test_bddf.py
    +++ b/python/bosdyn-core/tests/test_bddf.py
    @@ -53,9 +53,7 @@ def test_write_read():  # pylint: disable=too-many-statements,too-many-locals
     
             # Write POD data (floats) to the file.
             pod_writer = PodSeriesWriter(data_writer, pod_series_type, pod_spec, bddf.TYPE_FLOAT32,
    -                                     annotations={
    -                                         'units': 'm/s^2'
    -                                     })
    +                                     annotations={'units': 'm/s^2'})
             for val in range(10, 20):
                 pod_writer.write(timestamp_nsec, val)
     
    diff --git a/python/bosdyn-core/tests/test_geometry.py b/python/bosdyn-core/tests/test_geometry.py
    index 31030a373..d5f8eec0d 100644
    --- a/python/bosdyn-core/tests/test_geometry.py
    +++ b/python/bosdyn-core/tests/test_geometry.py
    @@ -19,15 +19,17 @@ def _dot(q1, q2):
         return q1.w * q2.w + q1.x * q2.x + q1.y * q2.y + q1.z * q2.z
     
     
    -@pytest.mark.parametrize(
    -    "yrp,wxyz",
    -    [([0.0, 0.0, 0.0], [1.0, 0.0, 0.0, 0.0]), ([math.pi, 0.0, 0.0], [0.0, 0.0, 0.0, 1.0]),
    -     ([0.0, math.pi, 0.0], [0.0, 1.0, 0.0, 0.0]), ([0.0, 0.0, math.pi], [0.0, 0.0, 1.0, 0.0]),
    -     ([-math.pi, 0.0, 0.0], [0.0, 0.0, 0.0, -1.0]), ([0.0, -math.pi, 0.0], [0.0, -1.0, 0.0, 0.0]),
    -     ([0.0, 0.0, -math.pi], [0.0, 0.0, -1.0, 0.0]),
    -     ([-0.25, -0.25, 0.25], [0.9748372, -0.1073143, 0.1381593, -0.1381593]),
    -     ([0.0, 0.0, 1.0], [0.8775826, 0, 0.4794255, 0]),
    -     ([1.3, 1.1, 1.2], [0.3815301, 0.052105, 0.6442849, 0.6607699])])
    +@pytest.mark.parametrize("yrp,wxyz",
    +                         [([0.0, 0.0, 0.0], [1.0, 0.0, 0.0, 0.0]),
    +                          ([math.pi, 0.0, 0.0], [0.0, 0.0, 0.0, 1.0]),
    +                          ([0.0, math.pi, 0.0], [0.0, 1.0, 0.0, 0.0]),
    +                          ([0.0, 0.0, math.pi], [0.0, 0.0, 1.0, 0.0]),
    +                          ([-math.pi, 0.0, 0.0], [0.0, 0.0, 0.0, -1.0]),
    +                          ([0.0, -math.pi, 0.0], [0.0, -1.0, 0.0, 0.0]),
    +                          ([0.0, 0.0, -math.pi], [0.0, 0.0, -1.0, 0.0]),
    +                          ([-0.25, -0.25, 0.25], [0.9748372, -0.1073143, 0.1381593, -0.1381593]),
    +                          ([0.0, 0.0, 1.0], [0.8775826, 0, 0.4794255, 0]),
    +                          ([1.3, 1.1, 1.2], [0.3815301, 0.052105, 0.6442849, 0.6607699])])
     def test_ypr_to_quaternion_proto(yrp, wxyz):
         euler_zxy = geometry.EulerZXY(yaw=yrp[0], roll=yrp[1], pitch=yrp[2])
         quat_initial = Quaternion(w=wxyz[0], x=wxyz[1], y=wxyz[2], z=wxyz[3])
    diff --git a/python/bosdyn-mission/src/bosdyn/mission/client.py b/python/bosdyn-mission/src/bosdyn/mission/client.py
    index 21738e86b..e02f0ba3b 100644
    --- a/python/bosdyn-mission/src/bosdyn/mission/client.py
    +++ b/python/bosdyn-mission/src/bosdyn/mission/client.py
    @@ -11,7 +11,7 @@
     
     from google.protobuf import timestamp_pb2
     
    -from bosdyn.client.common import BaseClient
    +from bosdyn.client.common import BaseClient, handle_lease_use_result_errors
     from bosdyn.client.common import (common_header_errors, handle_common_header_errors,
                                       handle_unset_status_error, error_factory)
     
    @@ -144,6 +144,22 @@ def load_mission_async(self, root, leases, **kwargs):
             return self.call_async(self._stub.LoadMission, req, None, _load_mission_error_from_response,
                                    **kwargs)
     
    +    def load_mission_as_chunks(self, root, leases, data_chunk_byte_size=1000*1000, **kwargs):
    +        """Load a mission onto the robot.
    +        Args:
    +            root: Root node in a mission.
    +            leases: All leases necessary to initialize a mission.
    +            data_chunk_byte_size: max size of each streamed message
    +        Raises:
    +            RpcError: Problem communicating with the robot.
    +            CompilationError: The mission failed to compile.
    +            bosdyn.mission.client.ValidationError: The mission failed to validate.
    +        """
    +        req = self._load_mission_request(root, leases)
    +        return self.call(self._stub.LoadMissionAsChunks,
    +                         BaseClient.chunk_message(req, data_chunk_byte_size), None,
    +                         _load_mission_error_from_response, **kwargs)
    +
         def play_mission(self, pause_time_secs, leases, settings=None, **kwargs):
             """Play the loaded mission.
     
    @@ -208,6 +224,23 @@ def pause_mission_async(self, **kwargs):
             return self.call_async(self._stub.PauseMission, req, None,
                                    _pause_mission_error_from_response, **kwargs)
     
    +    def stop_mission(self, **kwargs):
    +        """Stop the running mission.
    +
    +        Raises:
    +            RpcError: Problem communicating with the robot.
    +            NoMissionPlayingError: No mission playing.
    +        """
    +        req = self._stop_mission_request()
    +        return self.call(self._stub.StopMission, req, None, _stop_mission_error_from_response,
    +                         **kwargs)
    +
    +    def stop_mission_async(self, **kwargs):
    +        """Async version of stop_mission()."""
    +        req = self._stop_mission_request()
    +        return self.call_async(self._stub.StopMission, req, None,
    +                               _stop_mission_error_from_response, **kwargs)
    +
         def get_info(self, **kwargs):
             """Get static information about the loaded mission.
     
    @@ -278,6 +311,10 @@ def _restart_mission_request(self, pause_time_secs, leases, settings):
         def _pause_mission_request():
             return mission_pb2.PauseMissionRequest()
     
    +    @staticmethod
    +    def _stop_mission_request():
    +        return mission_pb2.StopMissionRequest()
    +
         @staticmethod
         def _get_info_request():
             return mission_pb2.GetInfoRequest()
    @@ -300,12 +337,12 @@ def _get_info_value(response):
     _ANSWER_QUESTION_STATUS_TO_ERROR = collections.defaultdict(lambda: (MissionResponseError, None))
     _ANSWER_QUESTION_STATUS_TO_ERROR.update({
         mission_pb2.AnswerQuestionResponse.STATUS_OK: (None, None),
    -    mission_pb2.AnswerQuestionResponse.STATUS_INVALID_QUESTION_ID: (InvalidQuestionId,
    -                                                                    InvalidQuestionId.__doc__),
    -    mission_pb2.AnswerQuestionResponse.STATUS_INVALID_CODE: (InvalidAnswerCode,
    -                                                             InvalidAnswerCode.__doc__),
    -    mission_pb2.AnswerQuestionResponse.STATUS_ALREADY_ANSWERED: (QuestionAlreadyAnswered,
    -                                                                 QuestionAlreadyAnswered.__doc__),
    +    mission_pb2.AnswerQuestionResponse.STATUS_INVALID_QUESTION_ID:
    +        (InvalidQuestionId, InvalidQuestionId.__doc__),
    +    mission_pb2.AnswerQuestionResponse.STATUS_INVALID_CODE:
    +        (InvalidAnswerCode, InvalidAnswerCode.__doc__),
    +    mission_pb2.AnswerQuestionResponse.STATUS_ALREADY_ANSWERED:
    +        (QuestionAlreadyAnswered, QuestionAlreadyAnswered.__doc__),
     })
     
     
    @@ -327,6 +364,7 @@ def _answer_question_error_from_response(response):
     
     @handle_common_header_errors
     @handle_unset_status_error(unset='STATUS_UNKNOWN')
    +@handle_lease_use_result_errors
     def _load_mission_error_from_response(response):
         return error_factory(response, response.status,
                              status_to_string=mission_pb2.LoadMissionResponse.Status.Name,
    @@ -342,6 +380,7 @@ def _load_mission_error_from_response(response):
     
     @handle_common_header_errors
     @handle_unset_status_error(unset='STATUS_UNKNOWN')
    +@handle_lease_use_result_errors
     def _play_mission_error_from_response(response):
         return error_factory(response, response.status,
                              status_to_string=mission_pb2.PlayMissionResponse.Status.Name,
    @@ -357,12 +396,29 @@ def _play_mission_error_from_response(response):
     
     @handle_common_header_errors
     @handle_unset_status_error(unset='STATUS_UNKNOWN')
    +@handle_lease_use_result_errors
     def _pause_mission_error_from_response(response):
         return error_factory(response, response.status,
                              status_to_string=mission_pb2.PauseMissionResponse.Status.Name,
                              status_to_error=_PAUSE_MISSION_STATUS_TO_ERROR)
     
     
    +_STOP_MISSION_STATUS_TO_ERROR = collections.defaultdict(lambda: (MissionResponseError, None))
    +_STOP_MISSION_STATUS_TO_ERROR.update({
    +    mission_pb2.StopMissionResponse.STATUS_OK: (None, None),
    +    mission_pb2.StopMissionResponse.STATUS_NO_MISSION_PLAYING: (NoMissionPlayingError, None),
    +})
    +
    +
    +@handle_common_header_errors
    +@handle_unset_status_error(unset='STATUS_UNKNOWN')
    +@handle_lease_use_result_errors
    +def _stop_mission_error_from_response(response):
    +    return error_factory(response, response.status,
    +                         status_to_string=mission_pb2.StopMissionResponse.Status.Name,
    +                         status_to_error=_STOP_MISSION_STATUS_TO_ERROR)
    +
    +
     _RESTART_MISSION_STATUS_TO_ERROR = collections.defaultdict(lambda: (MissionResponseError, None))
     _RESTART_MISSION_STATUS_TO_ERROR.update({
         mission_pb2.RestartMissionResponse.STATUS_OK: (None, None),
    @@ -373,6 +429,7 @@ def _pause_mission_error_from_response(response):
     
     @handle_common_header_errors
     @handle_unset_status_error(unset='STATUS_UNKNOWN')
    +@handle_lease_use_result_errors
     def _restart_mission_error_from_response(response):
         return error_factory(response, response.status,
                              status_to_string=mission_pb2.RestartMissionResponse.Status.Name,
    diff --git a/python/bosdyn-mission/src/bosdyn/mission/exceptions.py b/python/bosdyn-mission/src/bosdyn/mission/exceptions.py
    index 96213edd8..53698c867 100644
    --- a/python/bosdyn-mission/src/bosdyn/mission/exceptions.py
    +++ b/python/bosdyn-mission/src/bosdyn/mission/exceptions.py
    @@ -15,10 +15,50 @@ class Error(Exception):
     
     class CompileError(Error):
         """Error occurred during compilation."""
    +    def __init__(self, msg='', node_proto=None):
    +        Error.__init__(self, msg)
    +        self.node_proto = node_proto
    +
    +    def node_name(self):
    +        """Returns the 'name' field of the Node proto if possible, None otherwise"""
    +        if self.node_proto is None:
    +            return None
    +        try:
    +            return self.node_proto.name
    +        except Exception:
    +            return None
    +
    +    def node_impl(self):
    +        """Returns the proto type of the Node 'impl' field if possible, None otherwise"""
    +        if self.node_proto is None:
    +            return None
    +        try:
    +            return self.node_proto.impl.TypeName()
    +        except Exception:
    +            return None
    +
    +    def get_node_details(self):
    +        """Get a string of the node detail."""
    +        details = ''
    +        node_impl = self.node_impl()
    +        if node_impl:
    +            details = f' ({node_impl}'
    +
    +        node_name = self.node_name()
    +        if node_name is not None:
    +            if not details:
    +                details = ' (???'
    +            details += f' with name "{node_name}")'
    +        return details
    +
    +    def __str__(self):
    +        msg = super(CompileError, self).__str__()
    +        return msg + self.get_node_details()
     
     
     class UnknownType(CompileError):
    -    pass
    +    def __str__(self):
    +        return 'Do not know how to build {}'.format(self.node_impl())
     
     
     class ValidationError(Error):
    diff --git a/python/bosdyn-mission/src/bosdyn/mission/remote_client.py b/python/bosdyn-mission/src/bosdyn/mission/remote_client.py
    index d321b025c..259e0af0f 100644
    --- a/python/bosdyn-mission/src/bosdyn/mission/remote_client.py
    +++ b/python/bosdyn-mission/src/bosdyn/mission/remote_client.py
    @@ -134,10 +134,10 @@ def _session_id_from_response(response):
     _ESTABLISH_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
     _ESTABLISH_STATUS_TO_ERROR.update({
         remote_pb2.EstablishSessionResponse.STATUS_OK: (None, None),
    -    remote_pb2.EstablishSessionResponse.STATUS_MISSING_LEASES: (MissingLeases,
    -                                                                MissingLeases.__doc__),
    -    remote_pb2.EstablishSessionResponse.STATUS_MISSING_INPUTS: (MissingInputs,
    -                                                                MissingInputs.__doc__),
    +    remote_pb2.EstablishSessionResponse.STATUS_MISSING_LEASES:
    +        (MissingLeases, MissingLeases.__doc__),
    +    remote_pb2.EstablishSessionResponse.STATUS_MISSING_INPUTS:
    +        (MissingInputs, MissingInputs.__doc__),
     })
     
     
    @@ -188,8 +188,8 @@ def _stop_error_from_response(response):
     _TEARDOWN_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
     _TEARDOWN_STATUS_TO_ERROR.update({
         remote_pb2.TeardownSessionResponse.STATUS_OK: (None, None),
    -    remote_pb2.TeardownSessionResponse.STATUS_INVALID_SESSION_ID: (InvalidSessionId,
    -                                                                   InvalidSessionId.__doc__),
    +    remote_pb2.TeardownSessionResponse.STATUS_INVALID_SESSION_ID:
    +        (InvalidSessionId, InvalidSessionId.__doc__),
     })
     
     
    diff --git a/python/bosdyn-mission/src/bosdyn/mission/server_util.py b/python/bosdyn-mission/src/bosdyn/mission/server_util.py
    index 6de517cf7..da8467716 100644
    --- a/python/bosdyn-mission/src/bosdyn/mission/server_util.py
    +++ b/python/bosdyn-mission/src/bosdyn/mission/server_util.py
    @@ -10,11 +10,9 @@
     from deprecated import deprecated
     
     
    -@deprecated(
    -    reason='The ResponseContext helper class has moved to a common location. Please use '
    -           'bosdyn.client.util.ResponseContext.',
    -    version='2.3.5',
    -    action="always")
    +
    +@deprecated(reason='The ResponseContext helper class has moved to a common location. Please use '
    +            'bosdyn.client.server_util.ResponseContext.', version='2.3.5', action="always")
     class ResponseContext(object):
     
         def __init__(self, response, request, rpc_logger=None):
    @@ -36,13 +34,7 @@ def __exit__(self, exc_type, exc_val, exc_tb):
                 self.rpc_logger.add_protobuf_async(self.response)
     
     
    -def set_response_header(response, request, error_code=header_pb2.CommonError.CODE_OK,
    -                        error_message=None):
    -    """Sets the ResponseHeader header in the response."""
    -    header = header_pb2.ResponseHeader()
    -    header.request_received_timestamp = bosdyn.util.now_timestamp()
    -    header.request_header.CopyFrom(request.header)
    -    header.error.code = error_code
    -    if error_message:
    -        header.error.message = error_message
    -    response.header.CopyFrom(header)
    \ No newline at end of file
    +set_response_header = deprecated(
    +    reason='The bosdyn.mission.set_response_header helper class has moved to a common '
    +    'location. Please use bosdyn.client.server_util.populate_response_header.', version='3.0.0',
    +    action="always")(bosdyn.client.server_util.populate_response_header)
    diff --git a/python/bosdyn-mission/src/bosdyn/mission/util.py b/python/bosdyn-mission/src/bosdyn/mission/util.py
    index ea151f07c..95cdf8d63 100644
    --- a/python/bosdyn-mission/src/bosdyn/mission/util.py
    +++ b/python/bosdyn-mission/src/bosdyn/mission/util.py
    @@ -14,6 +14,7 @@
     import google.protobuf.text_format
     
     from bosdyn.api.mission import mission_pb2, nodes_pb2, util_pb2
    +from bosdyn.api.docking import docking_pb2
     from bosdyn.api.graph_nav import graph_nav_pb2
     from bosdyn.mission import constants
     
    @@ -33,7 +34,8 @@ def __init__(self, original_value, destination_typename):
             self.destination_typename = destination_typename
     
         def __str__(self):
    -        return 'Could not convert "{}" to type "{}"'.format(self.original_value, self.destination_typename)
    +        return 'Could not convert "{}" to type "{}"'.format(self.original_value,
    +                                                            self.destination_typename)
     
     
     _python_identifier_regex = re.compile('[A-Za-z_]\w*$')
    @@ -45,7 +47,8 @@ def tree_to_string(root, start_level=0, include_status=False):
         if start_level == 0:
             string += '\n'
         prefix = '|' + '-' * start_level
    -    string += prefix + text(root) + (' ' if text(root) else '') + '(' + root.__class__.__name__ + ')'
    +    string += prefix + text(root) + (' '
    +                                     if text(root) else '') + '(' + root.__class__.__name__ + ')'
         if include_status:
             string += '\n' + prefix + 'Status code: [{}]'.format(root.last_result)
         for child in root.children:
    @@ -121,21 +124,22 @@ def proto_from_tuple(tup):
         if hasattr(inner_proto, 'children'):
             if num_children == 0:
                 raise Error('Proto "{}" of type "{}" has no children!'.format(node.name, inner_type))
    +        for child_tup in children:
    +            child_node = proto_from_tuple(child_tup)
    +            inner_proto.children.add().CopyFrom(child_node)
         elif hasattr(inner_proto, 'child'):
    -        if num_children != 1:
    +        if isinstance(inner_proto, nodes_pb2.ForDuration) and num_children == 2:
    +            inner_proto.child.CopyFrom(proto_from_tuple(children[0]))
    +            inner_proto.timeout_child.CopyFrom(proto_from_tuple(children[1]))
    +        elif num_children == 1:
    +            inner_proto.child.CopyFrom(proto_from_tuple(children[0]))
    +        else:
                 raise Error('Proto "{}" of type "{}" has {} children!'.format(
                     node.name, inner_type, num_children))
         elif num_children != 0:
             raise Error('Proto "{}" of type "{}" was given {} children, but I do not know how to add'
                         ' them!'.format(node.name, inner_type, num_children))
     
    -    for child_tup in children:
    -        child_node = proto_from_tuple(child_tup)
    -        if hasattr(inner_proto, 'children'):
    -            inner_proto.children.add().CopyFrom(child_node)
    -        elif hasattr(inner_proto, 'child'):
    -            inner_proto.child.CopyFrom(child_node)
    -
         node.impl.Pack(inner_proto)
         return node
     
    @@ -249,7 +253,9 @@ def result_constant_to_proto_enum(result):
             raise InvalidConversion(result, util_pb2.Result.DESCRIPTOR.full_name)
     
     
    -def most_restrictive_travel_params(travel_params, vel_limit=None):
    +def most_restrictive_travel_params(travel_params, vel_limit=None,
    +                                   disable_directed_exploration=False,
    +                                   disable_alternate_route_finding=False):
         if travel_params is None:
             travel_params = graph_nav_pb2.TravelParams()
         else:
    @@ -281,6 +287,10 @@ def take_velocity_limit(returned, other):
     
         if vel_limit is not None:
             take_velocity_limit(travel_params.velocity_limit, vel_limit)
    +
    +    travel_params.disable_directed_exploration = travel_params.disable_directed_exploration or disable_directed_exploration
    +    travel_params.disable_alternate_route_finding = travel_params.disable_alternate_route_finding or disable_alternate_route_finding
    +
         return travel_params
     
     
    diff --git a/python/bosdyn-mission/tests/helpers.py b/python/bosdyn-mission/tests/helpers.py
    new file mode 100644
    index 000000000..d5130bfc6
    --- /dev/null
    +++ b/python/bosdyn-mission/tests/helpers.py
    @@ -0,0 +1,112 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""Common unit test helpers for bosdyn.mission tests."""
    +
    +
    +import concurrent
    +import grpc
    +from google.protobuf import timestamp_pb2
    +import sys
    +
    +import pytest
    +if sys.version_info[0:2] >= (3, 3):
    +    # Python version 3.3 added unittest.mock
    +    from unittest import mock
    +else:
    +    # The backport is on PyPi as just "mock"
    +    import mock
    +
    +import bosdyn.api.header_pb2 as HeaderProto
    +from bosdyn.client.robot_command import RobotCommandBuilder
    +import bosdyn.util
    +
    +from bosdyn.api import lease_pb2
    +
    +CONSTANT_TIMESTAMP = timestamp_pb2.Timestamp(seconds=12345, nanos=6789)
    +
    +
    +def setup_service_and_channel(service, service_adder):
    +    """Starts a service listening on a port and returns channel to it.
    +
    +    The service should have already been instantiated. It will be
    +    attached to a server listening on an ephemeral port and started.
    +
    +    Args:
    +        * service: The implementation of a gRPC service
    +        * service_adder: The function to add a service to a server. This is
    +        specified in the gRPC generated python, with a name like
    +        add_FooServiceServicer_to_server. Unfortunately, there's not an easy
    +        way to get to that method from the Service class.
    +    Returns:
    +        Channel to the server.
    +    """
    +    server = grpc.server(concurrent.futures.ThreadPoolExecutor(max_workers=1))
    +    service_adder(service, server)
    +    port = server.add_insecure_port('localhost:0')
    +    server.start()
    +    return server, grpc.insecure_channel('localhost:{}'.format(port))
    +
    +
    +def add_common_header(response, request, error_code=HeaderProto.CommonError.CODE_OK,
    +                      error_message=None):
    +    """Sets the common header on the response.
    +
    +    Args:
    +        response: The response object to fill the header with.
    +        request: The request to be echoed in the response common header.
    +        error_code: The code to use, OK by default.
    +        error_message: Any error message to include, empty by default.
    +    """
    +    header = HeaderProto.ResponseHeader()
    +    header.request_header.CopyFrom(request.header)
    +    header.error.code = error_code
    +    if error_message:
    +        header.error.message = error_message
    +    response.header.CopyFrom(header)
    +
    +
    +def build_stand_command(deprecated, **kwargs):
    +    if (deprecated):
    +        cmd = RobotCommandBuilder.stand_command(**kwargs)
    +    else:
    +        cmd = RobotCommandBuilder.synchro_stand_command(**kwargs)
    +    return cmd
    +
    +
    +def build_sit_command(deprecated):
    +    if (deprecated):
    +        cmd = RobotCommandBuilder.sit_command()
    +    else:
    +        cmd = RobotCommandBuilder.synchro_sit_command()
    +    return cmd
    +
    +
    +def set_converted_timestamp_skew(client, skew_sec):
    +    """Set the timestamp returned by the client's timesync endpoint."""
    +
    +    def get_ts(local_secs):
    +        return bosdyn.util.nsec_to_timestamp((local_secs + skew_sec) * bosdyn.util.NSEC_PER_SEC)
    +
    +    if client._timesync_endpoint is None:
    +        client._timesync_endpoint = mock.Mock()
    +    client._timesync_endpoint.robot_timestamp_from_local_secs = get_ts
    +
    +
    +def start_server(servicer_to_server, client, service, robot=None):
    +    """Build a server, add the given service to it, hook up the client, and start it."""
    +    server = grpc.server(concurrent.futures.ThreadPoolExecutor(max_workers=1))
    +    servicer_to_server(service, server)
    +    port = server.add_insecure_port('localhost:0')
    +    channel = grpc.insecure_channel('localhost:{}'.format(port))
    +    client.channel = channel
    +    server.start()
    +    return server
    +
    +def build_lease(sequence, epoch='foo', resource='bar'):
    +    lease = lease_pb2.Lease(resource=resource, epoch=epoch)
    +    lease.sequence.extend(sequence)
    +    return lease
    \ No newline at end of file
    diff --git a/python/bosdyn-mission/tests/test_client.py b/python/bosdyn-mission/tests/test_client.py
    index 8b2f842a8..67da25fab 100644
    --- a/python/bosdyn-mission/tests/test_client.py
    +++ b/python/bosdyn-mission/tests/test_client.py
    @@ -25,6 +25,7 @@
     from bosdyn.client.server_util import ResponseContext
     import bosdyn.mission.client
     
    +import helpers
     
     INVALID_ANSWER_CODE = 100
     INVALID_QUESTION_ID = -1
    @@ -131,13 +132,8 @@ def service():
     
     @pytest.fixture(scope='function')
     def server(client, service):
    -    server = grpc.server(concurrent.futures.ThreadPoolExecutor(max_workers=1))
    -    mission_service_pb2_grpc.add_MissionServiceServicer_to_server(service, server)
    -    port = server.add_insecure_port('localhost:0')
    -    channel = grpc.insecure_channel('localhost:{}'.format(port))
    -    client.channel = channel
    -    server.start()
    -    return server
    +    return helpers.start_server(mission_service_pb2_grpc.add_MissionServiceServicer_to_server,
    +                                client, service)
     
     
     def test_simple(client, server, service):
    @@ -156,6 +152,7 @@ def test_simple(client, server, service):
         assert resp.header.error.code == resp.header.error.CODE_OK
         assert resp.header.request_received_timestamp.seconds + resp.header.request_received_timestamp.nanos * 1e-9 > 0
     
    +
     def test_errors(client, server, service):
         """Test incorrect usage of the mission service client."""
         resp = client.get_state()
    diff --git a/python/examples/animation_recorder/README.md b/python/examples/animation_recorder/README.md
    new file mode 100644
    index 000000000..e22a5cbc4
    --- /dev/null
    +++ b/python/examples/animation_recorder/README.md
    @@ -0,0 +1,32 @@
    +
    +
    +# Animation Recorder
    +
    +This program can be run alongside tablet control of the robot to record, save and playback the motion the robot executes under tablet control. Animation Recorder records the robot using the Choreography Client logging service, which captures all the position and rotation information of the joints and body of the robot along with the time when they occurred. This information is then parsed into a *.cha animation file format. As a *.cha file, it contains all the information contained in the log, but can also be uploaded to the robot as an animated move which can then be turned into a choreography sequence. This choreography sequence containing the recorded move can then be played back on the robot. 
    +
    +## Understanding Spot Programming
    +For your best learning experience, please use the [Quickstart Guide](../../../docs/python/quickstart.md) found in the SDK's docs/python directory.  That will help you get your Python programming environment set up properly.  For understanding the Choreography Client look to the [Python Reference Guide](../../../python/bosdyn-choreography-client/src/bosdyn/choreography/client/choreography.md). For understanding Animation file formatting look to [Animation File Specification](../../../docs/concepts/choreography/animation_file_specification).
    +
    +## Setup Dependencies
    +See the requirements.txt file for a list of python dependencies which can be installed with pip using the command:
    +```
    +python3 -m pip install -r requirements.txt
    +```
    +
    +## Common Problems
    +1. Remember, you will need to be controlling the robot with the tablet before launching this program.
    +2. Make sure the Motor Enable button on the Spot rear panel is depressed.
    +3. The robot may power off during the upload progress for longer recorded animations. If this happens, you can power the robot back on using the tablet.
    +4. Start recording the robot only when it is in the basic upright standing position with all four feet on the ground, and avoid recording the robot sitting down, self-righting, or climbing stairs. Recording motions with any of these characteristics may cause the robot to fall during playback, or cause Animation Recorder to crash.
    +
    +## Run the Example
    +To run the example:
    +```
    +python3 animation_recorder.py --username USER --password PASSWORD --download-filepath FOLDERNAME  ROBOT_IP
    +```
    diff --git a/python/examples/animation_recorder/animation_recorder.py b/python/examples/animation_recorder/animation_recorder.py
    new file mode 100644
    index 000000000..ccbdc443b
    --- /dev/null
    +++ b/python/examples/animation_recorder/animation_recorder.py
    @@ -0,0 +1,534 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +
    +import curses
    +import logging
    +import signal
    +import time
    +import io
    +import os
    +import re
    +import hashlib
    +import argparse
    +import math
    +
    +from datetime import datetime
    +from google.protobuf import text_format
    +
    +import bosdyn.client.util
    +from bosdyn.choreography.client.choreography import ChoreographyClient, AnimationUploadHelper, AnimationValidationFailedError
    +from bosdyn.choreography.client.animation_file_conversion_helpers import *
    +from bosdyn.api.spot import choreography_sequence_pb2
    +from bosdyn.client import create_standard_sdk 
    +from bosdyn.client.lease import LeaseClient, LeaseKeepAlive
    +
    +from bosdyn.choreography.client.animation_file_to_proto import convert_animation_file_to_proto 
    +from bosdyn.choreography.client.animation_file_to_proto import (OPTIONS_KEYWORDS_TO_FUNCTION, GROUPED_HEADERS, 
    +                                                                SINGLE_HEADERS, COMMENT_DELIMITERS)
    +
    +COMMAND_INPUT_RATE = 0.1
    +LOGGER = logging.getLogger(__name__)
    +
    +class RecorderInterface(object):
    +    """Creates and plays animations on the robot by recording a robot being controlled by the tablet.
    +
    +    Args:
    +        robot(robot): The incoming robot object to be recorded and controlled.
    +        download_filepath(string): Location where animation_recorder.py is located, used when saving and reading files.
    +    """
    +    def __init__(self, robot, download_filepath):
    +        self._robot = robot
    +
    +        # Helper class for uploading animation files to the robot
    +        self._choreography_animation_helper = AnimationUploadHelper(self._robot)
    +
    +        # ensure that the robot will be able to use the ChoreographyClient
    +        self._choreo_client = robot.ensure_client(ChoreographyClient.default_service_name)
    +
    +        # Create clients -- do not use the for communication yet.
    +        self._lease_client = robot.ensure_client(LeaseClient.default_service_name)
    +
    +        # filepath to animation_recorder.py
    +        self._download_filepath = download_filepath
    +
    +        # name of the sub directory where animation files are saved and read
    +        self._animation_directory = ""
    +
    +        # variables for tracking when an animated sequence finishes so the lease can be returned when it finishes, 
    +        # with _start_time being when the animation started and _length_of_current_choreo being the duration of the 
    +        # selected animation being played back
    +        self._length_of_current_choreo = 0
    +        self._start_time = 0
    +
    +        # variable to keep track of the subset of the animation files list to display as options on 
    +        # the playback screen
    +        self._page = 0
    +
    +        # list to hold the names of the animation files
    +        self._proto_names = [] 
    +
    +        # list containing the choreographys generated from the animation files in _proto_names
    +        self._choreos = []
    +        
    +        # variable for keeping track of the current lease when Animation Recorder is controlling the robot
    +        self._lease = None
    +
    +        # variable for keeping track of the leaseKeepAlive instance when the lease is being controlled by Animation Recorder
    +        self._lease_alive = None
    +
    +        # variable for determining if the program should quit
    +        self._exit_check = None
    +
    +        # determine the screen to display:
    +        # 0 : main
    +        # 1 : save or discard the recorded animation
    +        # 2 : playback an option from the list of previously recorded animations
    +        self._screen = 0    # on start display the main screen
    +
    +        # keep track of whether the program is recording or not recording
    +        self._recording = False
    +
    +        # Check that an estop is connected with the robot so that the robot commands can be executed.
    +        # The E-Stop while using Animation Recorder should be the tablet.
    +        assert not robot.is_estopped(), "Robot is estopped. Please connect the robot to the tablet or use " \
    +                                        "the estop SDK example, to configure E-Stop."
    +
    +
    +        self._command_dictionary = {
    +            # list of viable key commands for the main screen
    +            ord('\t'): self._quit_program,
    +            ord('f'): self._start_recording,
    +            ord('g'): self._stop_recording,
    +            ord('o'): self._load_screen
    +        }
    +
    +        self._question_command_dictionary = {
    +            # list of viable key commands for the save screen
    +            ord('y'): self._yes,
    +            ord('n'): self._no
    +        }
    +
    +        self._load_command_dictionary = {
    +            # list of viable key commands for the playback screen
    +            ord('\t'): (self._done, 0),
    +            ord('1'): (self._choose_file, 0),
    +            ord('2'): (self._choose_file, 1),
    +            ord('3'): (self._choose_file, 2),
    +            ord('4'): (self._choose_file, 3),
    +            ord('5'): (self._choose_file, 4),
    +            ord('.'): (self._next, 0),
    +            ord(','): (self._back, 0)
    +        }
    +
    +    def _quit_program(self):
    +        """Stop running the animation recorder api."""
    +        if self._exit_check is not None:
    +            self._exit_check.request_exit() 
    +
    +    def _start_recording(self):
    +        """Start recording the robot."""
    +        recording_length = 300  # Here recording length is set to 300 seconds (5 minutes), the maximum recording length. 
    +        self._recording = True
    +        self._choreo_client.start_recording_state(recording_length)   
    +
    +    def _is_recording(self):
    +        """Returns the recording state as a string to be displayed on the main screen telling 
    +        the user whether the program is currently recording animation.
    +        """
    +        if(self._recording):
    +            return "Recording"
    +        return "Not Recording"
    +     
    +    def _stop_recording(self):
    +        """Stop recording the robot."""
    +        self._choreo_client.stop_recording_state()
    +        if not self._recording:
    +            return True
    +        self._recording = False
    +        self._screen = 1
    +   
    +    def _yes(self):
    +        """Save the recording as an animation file and upload the related choreography to the robot."""
    +        self._screen = 0
    +        
    +        # create a unique name for the new animated recording
    +        now = datetime.now()
    +        dtime = now.strftime("%H%M%S")
    +        dday = now.strftime("%d%m%Y")
    +        name = dday + dtime
    +
    +        # create the animation *.cha file from the choreography log and save it to the animation directory
    +        cha_path = os.path.join(self._download_filepath, self._animation_directory)
    +        cha_filename = self._choreo_client.choreography_log_to_animation_file(name, cha_path, self._robot.has_arm(), "truncatable")
    +
    +        # upload the animation to the robot as a choreography sequence from the *.cha file and list
    +        # the animation for playback
    +        animation, choreography_seq = self.proto_to_choreo(cha_filename)
    +        self._proto_names.append(animation.name)
    +        self._choreos.append(choreography_seq)
    +
    +    def _no(self):
    +        """Switches from the save screen to the main screen."""
    +        self._screen = 0
    +
    +    def _load_screen(self):
    +        """Switches from the main screen to the playback screen."""
    +        self._screen = 2
    +
    +    def _done(self, i):
    +        """Switches from the playback screen to the main screen."""
    +        self._screen = 0
    +
    +    def _choose_file(self, i):
    +        """Ensure that chosen file has a valid animation at that index, then play the selected animation file."""
    +        if(len(self._proto_names) > i + (self._page * 5)):
    +            self._play_pre_recorded(i)
    +
    +    def _next(self, i):
    +        """Display the next 5 animation files in the animation file list."""
    +        if self._page < len(self._proto_names)/5 :
    +            self._page += 1
    +
    +    def _back(self, i):
    +        """Display the previous 5 animation files in the animation file list."""
    +        if self._page > 0 :
    +            self._page -= 1
    +
    +    
    +    def _upload_animation_proto(self, animation):
    +        """Helper function for uploading an animation protobuf to the robot while providing debug messages."""
    +        upload_response = None
    +        try:
    +            upload_response = self._choreography_animation_helper.upload_animated_move(animation.proto)
    +            # The python choreography client will throw an exception if the upload animated move rpc returns
    +            # a status code that isn't STATUS_OK.
    +            if upload_response is None:
    +                # The upload animation RPC may not be issued at all if the animation hasn't changed
    +                # and doesn't need to be sent to the robot. In that case, the response is None but we
    +                # still return true to indicate the move is successfully placed on robot.
    +                LOGGER.info("Success: %s (Animation is already uploaded to the robot)." % animation.name)
    +                return True
    +            LOGGER.info("Success: %s (Uploaded new/changed animation)." % animation.name)
    +        except AnimationValidationFailedError as validation_failed:
    +            upload_response = validation_failed.response
    +        except Exception as err:
    +            error_msg = "Failed to upload animation %s: [%s] %s" % (animation.name, str(
    +                type(err)), str(err))
    +            LOGGER.warning(error_msg)
    +            return False
    +
    +        if upload_response is not None:
    +            if len(upload_response.warnings) > 0:
    +                error_msg = "Animation '" + str(
    +                    animation.name) + "' upload failed. The following warnings were produced: \n"
    +                for warn in upload_response.warnings:
    +                    error_msg += "\t"
    +                    error_msg += warn
    +                    LOGGER.warning(error_msg)
    +
    +        LOGGER.info(upload_response.status)
    +        return upload_response.status == choreography_sequence_pb2.UploadAnimatedMoveResponse.STATUS_OK
    +
    +    def proto_to_choreo(self, cha_filename):
    +        """Function for uploading the animation file as a protobuf to the robot and then turning the animation
    +        into a choreography sequence which can then be played on the robot.
    +
    +        Args:
    +            cha_filename (string): Name of the *.cha animation file to be uploaded to the robot.
    +        
    +        Returns:
    +            The animation protobuf name. 
    +            The choreography sequence containing the animation move described by the *.cha animation file.
    +        """
    +
    +        # path to the *.cha animation file to be uploaded
    +        fpath = os.path.join(self._download_filepath, self._animation_directory)
    +        animated_file = os.path.join(fpath, cha_filename)
    +
    +        # use the *.cha animation file to create an animation protobuf file. convert_animation_file_to_proto can take a second
    +        # optional argument for a filepath to a file containing the default values for animation parameter fields, but Animation 
    +        # Recorder doesn't produce any animations that have parameter fields so only the *.cha animation file is needed.
    +        animation = convert_animation_file_to_proto(animated_file)
    +
    +        #upload the created animation protobuf to the robot
    +        self._upload_animation_proto(animation)
    +
    +        # turn animation into a choreography sequence:
    +        # initialize a ChoreographySequence object 
    +        choreography_seq = choreography_sequence_pb2.ChoreographySequence()
    +        # assign the ChoreographySequence name to be the animation protobuf name
    +        choreography_seq.name = animation.name
    +        # assign the playback speed of the choreography sequence
    +        choreography_seq.slices_per_minute = 200 * 4
    +        
    +        # the MoveParamsList will contain the list of moves making up the choreography sequence. For Animation Recorder
    +        # the choreography sequences are one move long, where the move is the motion captured by the recording.
    +        MoveParamsList = []
    +
    +        move_param = choreography_sequence_pb2.MoveParams()
    +        move_param.type = "animation"
    +
    +        # start the move immediately with no delay
    +        move_param.start_slice = 0
    +
    +        # calculate the expected duration of the move in slices by multiplying the duration of the animation in minutes by the 
    +        # number of slices per minute of the created choreography sequence. 
    +        move_param.requested_slices = math.ceil(((animation.proto.animation_keyframes[-1].time - 
    +                                        animation.proto.animation_keyframes[0].time)/ 60) * choreography_seq.slices_per_minute)
    +
    +        # assign the move the name of the animation protobuf
    +        move_param.animate_params.animation_name = animation.name
    +        
    +        # add the move to the list of moves 
    +        MoveParamsList.append(move_param)
    +
    +        # add the list of moves to the choreography sequence
    +        choreography_seq.moves.extend(MoveParamsList)
    +
    +        # upload the choreography sequence to the robot
    +        upload_response = self._choreo_client.upload_choreography(choreography_seq, non_strict_parsing=True)
    +
    +        # return the animation protobuf and choreography sequence
    +        return animation, choreography_seq
    +
    +    def _play_pre_recorded(self, i):
    +        """Play the animation file at index i in the animation files list."""
    +        start_time = None
    +
    +        # lease needs to be forcibly taken because the tablet is actively controlling the robot
    +        self._take_lease()
    +        self._length_of_current_choreo = self._choreos[self._page * 5 + i].moves[0].requested_slices / self._choreos[self._page * 5 + i].slices_per_minute
    +        self._start_time = self._time_in_secs()
    +
    +        upload_response = self._choreo_client.upload_choreography(self._choreos[self._page * 5 + i], non_strict_parsing=True)
    +        self._choreo_client.execute_choreography(self._proto_names[self._page * 5 + i], start_time, 0)
    +
    +
    +
    +    def _take_lease(self):
    +        """Begin communication with the robot."""
    +        # take the lease from the tablet
    +        self._lease = self._lease_client.take()
    +
    +        # Construct our lease keep-alive object, which begins RetainLease calls in a thread.
    +        self._lease_alive = LeaseKeepAlive(self._lease_client, warnings=False)
    +
    +    def _time_in_secs(self):
    +        """Helper function for getting real time in seconds. Used to measure when a choreography has ended."""
    +        now = datetime.now()
    +        dtime_s, dtime_m, dtime_h = int(now.strftime("%S")), int(now.strftime("%M")), int(now.strftime("%H"))
    +        t = dtime_s + 60*dtime_m + 60*60*dtime_h
    +        return t
    +
    +    def _return_lease(self):
    +        """Function to return a lease being used by Animation Recorder."""
    +        self._lease_client.return_lease(self._lease)
    +    
    +
    +
    +    def drive(self, stdscr):
    +        """Determine when to give back the lease and draw the curses screen while the program is active."""
    +        
    +        f_time = self._time_in_secs()
    +
    +        # if the current time exceeds the time when an animation running on the robot was expected to finish,
    +        # give back the lease
    +        if(self._length_of_current_choreo != 0 and f_time > self._length_of_current_choreo + self._start_time):
    +            self._length_of_current_choreo = 0
    +            self._return_lease()
    +        
    +        with ExitCheck() as self._exit_check:
    +
    +            stdscr.nodelay(True)  # Don't block for user input.
    +            stdscr.resize(26, 96)
    +            stdscr.refresh()
    +
    +            # allow user input to appear on the curses interface
    +            curses.echo()
    +
    +            try:
    +                while not self._exit_check.kill_now:
    +                    # draw the interface 
    +                    self._drive_draw(stdscr)
    +                    try:
    +                        # take in user commands from keypress input
    +                        cmd = stdscr.getch()
    +                        # Do not queue up commands on client
    +                        self._drive_cmd(cmd)
    +                        time.sleep(COMMAND_INPUT_RATE)
    +                    except Exception:
    +                        time.sleep(2.0)
    +                        raise
    +            finally:
    +                stdscr.clear()
    +
    +    def _drive_draw(self, stdscr):
    +
    +        if(self._screen == 0):
    +             self._main(stdscr)
    +        
    +        elif(self._screen == 1):
    +            self._question(stdscr)
    +
    +        elif(self._screen == 2):
    +            self._load(stdscr)
    +
    +    def _drive_cmd(self, key):
    +        """Run user commands at each update."""
    +        try:
    +            if(self._screen != 2):
    +
    +                if(self._screen == 0):
    +                    cmd_function = self._command_dictionary[key]
    +                elif(self._screen == 1):
    +                    cmd_function = self._question_command_dictionary[key]
    +                cmd_function()
    +
    +            else:
    +                (cmd_function, i)= self._load_command_dictionary[key]
    +                cmd_function(i)
    +            
    +        except KeyError:
    +            if key and key != -1 and key < 256:
    +                # self.add_message("Unrecognized keyboard command: '{}'".format(chr(key)))
    +                print("Unrecognized keyboard command.")
    +                time.sleep(2.0)
    +
    +    def _main(self, stdscr):
    +        """Helper function for the _drive_draw function. Draw the main interface screen at each update."""
    +        stdscr.clear()  # clear screen
    +        stdscr.resize(26, 50)
    +
    +        recording_state = self._is_recording()
    +        stdscr.addstr(11, 15, recording_state)
    +
    +        stdscr.addstr(3, 5, "Commands: [TAB]: Quit")
    +        stdscr.addstr(4, 5, "          [f]: Record Choreography")
    +        stdscr.addstr(5, 5, "          [g]: Stop Recording Choreography")
    +        stdscr.addstr(6, 5, "          [o]: Playback")   
    +            
    +        stdscr.refresh()
    +
    +    def _question(self, stdscr):
    +        """Helper function for the _drive_draw function. Draw the save or discard interface screen at each update."""
    +        stdscr.clear()  # clear screen
    +        stdscr.resize(26, 50)
    +
    +        stdscr.addstr(4, 5, "save choreography?")
    +        stdscr.addstr(5, 5, "          [y]: Save Choreography")
    +        stdscr.addstr(6, 5, "          [n]: Discard Recording")
    +        
    +        stdscr.refresh()
    +
    +    def _load(self, stdscr):
    +        """Helper function for the _drive_draw function. Draw the playback options interface screen at each update."""
    +        stdscr.clear()  # clear screen
    +        stdscr.resize(26, 50)
    +
    +        stdscr.addstr(20, 5, "                                       " + str(self._page))
    +        stdscr.addstr(4, 5, "Choose a file:")
    +
    +        # write the animation options for the current page from the animation files list
    +        for i in range(0,5):
    +            if len(self._proto_names) > (i + (self._page * 5)):
    +                stdscr.addstr(5 + i, 5, "          [" + str(i+1) + "]: " + self._proto_names[i + (self._page * 5)])
    +            else:
    +                stdscr.addstr(5 + i, 5, "          [" + str(i+1) + "]: ")
    +
    +        
    +        stdscr.addstr(10, 5, "          [>]: Increment Page +")
    +        stdscr.addstr(11, 5, "          [<]: Increment Page -")
    +        stdscr.addstr(12, 5, "          [TAB]: Return to Main")
    +         
    +        stdscr.refresh()
    +
    +
    +class ExitCheck(object):
    +    """A class to help exiting a loop, also capturing SIGTERM to exit the loop."""
    +
    +    def __init__(self):
    +        self._kill_now = False
    +        signal.signal(signal.SIGTERM, self._sigterm_handler)
    +        signal.signal(signal.SIGINT, self._sigterm_handler)
    +
    +    def __enter__(self):
    +        return self
    +
    +    def __exit__(self, _type, _value, _traceback):
    +        return False
    +
    +    def _sigterm_handler(self, _signum, _frame):
    +        self._kill_now = True
    +
    +    def request_exit(self):
    +        """Manually trigger an exit (rather than sigterm/sigint)."""
    +        self._kill_now = True
    +
    +    @property
    +    def kill_now(self):
    +        """Return the status of the exit checker indicating if it should exit."""
    +        return self._kill_now
    +
    +def main():
    +
    +    parser = argparse.ArgumentParser()
    +    bosdyn.client.util.add_common_arguments(parser)
    +    parser.add_argument('-d', '--download-filepath',
    +                        help='Name for the new directory where recorded *.cha animation files will be read and saved',
    +                        default=os.getcwd())
    +    options = parser.parse_args()
    +
    +    default_filepath = os.path.dirname(os.path.realpath(__file__))
    +   
    +    # create an sdk 
    +    sdk = bosdyn.client.create_standard_sdk('AnimationRecorder')
    +
    +    # Create robot object with the ability to access the ChoreographyClient
    +    sdk.register_service_client(ChoreographyClient)
    +    robot = sdk.create_robot(options.hostname)
    +    robot.authenticate(options.username, options.password)
    +
    +    recorder_interface = RecorderInterface(robot, default_filepath)
    +    
    +    # create the subdirectory for recorded animation files, with a default directory name of 'recorded_animations [DATE: DAY-MONTH-YEAR]'
    +    if len(options.download_filepath) == 0:
    +        now = datetime.now()
    +        options.download_filepath = "recorded_animations_" + now.strftime("%d-%m-%Y")
    +    
    +    recorder_interface._animation_directory = options.download_filepath
    +    fpath = os.path.join(recorder_interface._download_filepath, recorder_interface._animation_directory)
    +
    +    if not os.path.isdir(fpath): 
    +        # create the new subdirectory with the chosen name
    +        os.mkdir(fpath)
    +        print("recorded animations directory created : " + options.download_filepath)
    +    else: 
    +        # if the folder already existed save files in the existing subdirectory
    +        print("recorded animations will be saved to directory : " + options.download_filepath)
    +    
    +    
    +    # Run the program with curses user interface
    +    try:
    +        # Prevent curses from introducing a 1 second delay for ESC key
    +        os.environ.setdefault('ESCDELAY', '0')
    +        # Run animation recorder interface in curses mode, then restore terminal config.
    +        curses.wrapper(recorder_interface.drive)
    +       
    +    except Exception as e:
    +        LOGGER.error("Animation Recorder has thrown an error: [%r] %s", e, e)
    +
    +    finally:
    +        # Do any final cleanup steps.
    +        recorder_interface._quit_program()
    +
    +    return True
    +
    +
    +if __name__ == "__main__":
    +    if not main():
    +        os._exit(1)
    +    os._exit(0)
    diff --git a/python/examples/animation_recorder/requirements.txt b/python/examples/animation_recorder/requirements.txt
    new file mode 100644
    index 000000000..ced920e02
    --- /dev/null
    +++ b/python/examples/animation_recorder/requirements.txt
    @@ -0,0 +1,6 @@
    +-f ../../../prebuilt
    +
    +bosdyn-client >= 3.0
    +bosdyn-choreography-client >= 3.0
    +
    +windows-curses==2.1.0; sys_platform == 'win32'
    diff --git a/python/examples/arm_and_mobility_command/requirements.txt b/python/examples/arm_and_mobility_command/requirements.txt
    index 5f0983252..ea085f145 100644
    --- a/python/examples/arm_and_mobility_command/requirements.txt
    +++ b/python/examples/arm_and_mobility_command/requirements.txt
    @@ -1,2 +1,2 @@
     -f ../../../prebuilt
    -bosdyn-client ~= 2.3
    +bosdyn-client >= 2.3
    diff --git a/python/examples/arm_constrained_manipulation/README.md b/python/examples/arm_constrained_manipulation/README.md
    new file mode 100644
    index 000000000..30606b3bb
    --- /dev/null
    +++ b/python/examples/arm_constrained_manipulation/README.md
    @@ -0,0 +1,55 @@
    +
    +
    +# Constrained Manipulation
    +The examples here demonstrate how to use constrained manipulation 
    +through the API. This feature is used to manipulate objects that
    +are constrained by the environment, and move in a low-dimensional space.
    +Examples include ball valves, switches, cabinets and drawers.
    +
    +## Setup Dependencies
    +These examples need to be run with python3, and have the Spot SDK installed.
    +
    +## Constrained Manipulation Example
    +Before running this example, we need to setup the
    +grasp of the object. Once the object is successfully grasped, 
    +we can run the constrained manipulation example.
    +
    +### Setup Robot and grasp and object
    +
    +To properly setup for this example:
    +1. Use an external E-Stop endpoint from an api client or tablet
    +2. Power the robot on
    +3. Use the API or the tablet to drive the robot close to the object
    +   of interest, for e.g. a ball valve, cabinet, etc.
    +4. Grasp the object using the API scripts or the tablet
    +5. Run the run_constrained_manipulation.py script to manipulate 
    +   constrained object.
    +
    +### Running the Example
    +
    +When run, this script will take over the lease
    +and perform manipulation for the task specified 
    +in the script for a fixed duration of time. 
    +
    +You can construct your task type of interest by calling one of the
    +functions in constrained_manipulation_helper.py for e.g.
    +construct_crank_task(velocity_normalized)
    +in the run_constrained_manipulation.py script.
    +
    +You can specify the task type, the velocity along the task
    +and the force or torque limits as arguments. Note that for 
    +all tasks besides the knob task type, the velocity is a 
    +normalized velocity in the range [-1, 1].
    +Please look in the file constrained_manipulation_helper.py
    +for more details on how the velocity is scaled with the force limit.
    +
    +```
    +python3 run_constrained_manipulation.py --username USER --password PASSWORD ROBOT_IP --task-type crank  --task-velocity 0.5 --force-limit 40
    +
    +```
    diff --git a/python/examples/arm_constrained_manipulation/constrained_manipulation_helper.py b/python/examples/arm_constrained_manipulation/constrained_manipulation_helper.py
    new file mode 100644
    index 000000000..9226dbb29
    --- /dev/null
    +++ b/python/examples/arm_constrained_manipulation/constrained_manipulation_helper.py
    @@ -0,0 +1,326 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""Test script to run constrained manipulation
    +"""
    +
    +from bosdyn.client.robot_command import RobotCommandBuilder
    +from bosdyn.api import geometry_pb2
    +from bosdyn.api import basic_command_pb2
    +
    +def construct_lever_task(velocity_normalized, force_limit=40, torque_limit=5):
    +    """ Helper function for manipulating levers
    +
    +    params:
    +    + velocity_normalized: normalized task tangential velocity in range [-1.0, 1.0]
    +    + force_limit (optional): positive value denoting max force robot will exert along task dimension
    +    + torque_limit (optional): positive value denoting max torque robot will exert along
    +                            the axis of rotation of the task
    +
    +    Output:
    +    + command: api command object
    +
    +    Notes:
    +    In this function, we assume the initial motion of the lever is
    +    along the z axis of the hand (up and down). If the initial
    +    grasp is such that the initial motion needs to be something else,
    +    change the force direction.
    +    This function assumes we don't know the plane_normal (i.e. torque_direction)
    +    of the lever. If we do know that, we can specify it as torque_direction
    +    or use the ball valve task types, which assume a specific grasp and specify
    +    what the initial torque_direction is.
    +    """
    +    velocity_limit = scale_velocity_lim_given_force_lim(force_limit)
    +    tangential_velocity = velocity_normalized * velocity_limit
    +    frame_name = "hand"
    +    force_lim = force_limit
    +    torque_lim = torque_limit
    +    force_direction = geometry_pb2.Vec3(x=0.0, y=0.0, z=1.0)
    +    torque_direction = geometry_pb2.Vec3(x=0.0, y=0.0, z=0.0)
    +    init_wrench_dir = geometry_pb2.Wrench(force=force_direction, torque=torque_direction)
    +    task_type = basic_command_pb2.ConstrainedManipulationCommand.Request.TASK_TYPE_SE3_CIRCLE_FORCE_TORQUE
    +    
    +    command = RobotCommandBuilder.constrained_manipulation_command(
    +        task_type=task_type, init_wrench_direction_in_frame_name=init_wrench_dir, force_limit=force_lim,
    +        torque_limit=torque_lim, tangential_speed=tangential_velocity, frame_name=frame_name)
    +    return command
    +
    +def construct_right_handed_ballvalve_task(velocity_normalized, force_limit=40, torque_limit=5):
    +    """ Helper function for manipulating right-handed ball valves
    +    Use this when the hand is to the right of the pivot of the ball valve
    +    And when hand x axis is roughly parallel to the axis of rotation of
    +    the ball valve
    +
    +    params:
    +    + velocity_normalized: normalized task tangential velocity in range [-1.0, 1.0]
    +    + force_limit (optional): positive value denoting max force robot will exert along task dimension
    +    + torque_limit (optional): positive value denoting max torque robot will exert along
    +                            the axis of rotation of the task
    +
    +    Output:
    +    + command: api command object
    +
    +    Notes:
    +    If the grasp is such that the hand x axis is not parallel to the axis
    +    of rotation of the ball valve, then use the lever task.
    +    """
    +    velocity_limit = scale_velocity_lim_given_force_lim(force_limit)
    +    tangential_velocity = velocity_normalized * velocity_limit
    +    frame_name = "hand"
    +    force_lim = force_limit
    +    torque_lim = torque_limit
    +    # Force/torque signs are opposite for right-handed ball valve
    +    force_direction = geometry_pb2.Vec3(x=0.0, y=0.0, z=1.0)
    +    # The torque vector is provided as additional information denoting the
    +    # axis of rotation of the task.
    +    torque_direction = geometry_pb2.Vec3(x=-1.0, y=0.0, z=0.0)
    +    init_wrench_dir = geometry_pb2.Wrench(force=force_direction, torque=torque_direction)
    +    task_type = basic_command_pb2.ConstrainedManipulationCommand.Request.TASK_TYPE_SE3_CIRCLE_FORCE_TORQUE
    +    
    +    command = RobotCommandBuilder.constrained_manipulation_command(
    +        task_type=task_type, init_wrench_direction_in_frame_name=init_wrench_dir, force_limit=force_lim,
    +        torque_limit=torque_lim, tangential_speed=tangential_velocity, frame_name=frame_name)
    +    return command
    +
    +
    +def construct_left_handed_ballvalve_task(velocity_normalized, force_limit=40, torque_limit=5):
    +    """ Helper function for manipulating left-handed ball valves
    +    Use this when the hand is to the left of the pivot of the ball valve
    +    And when hand x axis is roughly parallel to the axis of rotation of
    +    the ball valve
    +
    +    params:
    +    + velocity_normalized: normalized task tangential velocity in range [-1.0, 1.0]
    +    + force_limit (optional): positive value denoting max force robot will exert along task dimension
    +    + torque_limit (optional): positive value denoting max torque robot will exert along
    +                            the axis of rotation of the task
    +
    +    Output:
    +    + command: api command object
    +
    +    Notes:
    +    If the grasp is such that the hand x axis is not parallel to the axis
    +    of rotation of the ball valve, then use the lever task.
    +    """
    +    velocity_limit = scale_velocity_lim_given_force_lim(force_limit)
    +    tangential_velocity = velocity_normalized * velocity_limit
    +    frame_name = "hand"
    +    force_lim = force_limit
    +    torque_lim = torque_limit
    +    # Force/torque signs are the same for left-handed ball valve
    +    force_direction = geometry_pb2.Vec3(x=0.0, y=0.0, z=1.0)
    +    # The torque vector is provided as additional information denoting the
    +    # axis of rotation of the task.
    +    torque_direction = geometry_pb2.Vec3(x=1.0, y=0.0, z=0.0)
    +    init_wrench_dir = geometry_pb2.Wrench(force=force_direction, torque=torque_direction)
    +    task_type = basic_command_pb2.ConstrainedManipulationCommand.Request.TASK_TYPE_SE3_CIRCLE_FORCE_TORQUE
    +
    +    command = RobotCommandBuilder.constrained_manipulation_command(
    +        task_type=task_type, init_wrench_direction_in_frame_name=init_wrench_dir, force_limit=force_lim,
    +        torque_limit=torque_lim, tangential_speed=tangential_velocity, frame_name=frame_name)
    +    return command
    +
    +
    +def construct_crank_task(velocity_normalized, force_limit=40):
    +    """ Helper function for manipulating cranks with a free to rotate handle
    +
    +    params:
    +    + velocity_normalized: normalized task tangential velocity in range [-1.0, 1.0]
    +    + force_limit (optional): positive value denoting max force robot will exert along task dimension
    +
    +    Output:
    +    + command: api command object
    +
    +    Notes:
    +    In this function, we assume the initial motion of the crank is
    +    along the y axis of the hand (left and right). If the initial
    +    grasp is such that the initial motion needs to be something else,
    +    change the force direction.
    +    """
    +    velocity_limit = scale_velocity_lim_given_force_lim(force_limit)
    +    tangential_velocity = velocity_normalized * velocity_limit
    +    frame_name = "hand"
    +    force_lim = force_limit
    +    # Setting a placeholder value that doesn't matter, since we don't
    +    # apply a pure torque in this task.
    +    torque_lim = 5.0
    +    # This assumes the grasp of crank is such that the crank will initially 
    +    # move along the hand y axis. Change if that is not the case.
    +    force_direction = geometry_pb2.Vec3(x=0.0, y=1.0, z=0.0)
    +    torque_direction = geometry_pb2.Vec3(x=0.0, y=0.0, z=0.0)
    +    init_wrench_dir = geometry_pb2.Wrench(force=force_direction, torque=torque_direction)
    +    task_type = basic_command_pb2.ConstrainedManipulationCommand.Request.TASK_TYPE_R3_CIRCLE_EXTRADOF_FORCE
    +
    +    command = RobotCommandBuilder.constrained_manipulation_command(
    +        task_type=task_type, init_wrench_direction_in_frame_name=init_wrench_dir, force_limit=force_lim,
    +        torque_limit=torque_lim, tangential_speed=tangential_velocity, frame_name=frame_name)
    +
    +    return command
    +
    +
    +def construct_cabinet_task(velocity_normalized, force_limit=40):
    +    """ Helper function for opening/closing cabinets
    +
    +    params:
    +    + velocity_normalized: normalized task tangential velocity in range [-1.0, 1.0]
    +    + force_limit (optional): positive value denoting max force robot will exert along task dimension
    +
    +    Output:
    +    + command: api command object
    +
    +    Notes:
    +    In this function, we assume the initial motion of the cabinet is
    +    along the x axis of the hand (forward and backward). If the initial
    +    grasp is such that the initial motion needs to be something else,
    +    change the force direction.
    +    """
    +    velocity_limit = scale_velocity_lim_given_force_lim(force_limit)
    +    tangential_velocity = velocity_normalized * velocity_limit
    +    frame_name = "hand"
    +    force_lim = force_limit
    +    # Setting a placeholder value that doesn't matter, since we don't
    +    # apply a pure torque in this task.
    +    torque_lim = 5.0
    +    force_direction = geometry_pb2.Vec3(x=1.0, y=0.0, z=0.0)
    +    torque_direction = geometry_pb2.Vec3(x=0.0, y=0.0, z=0.0)
    +    init_wrench_dir = geometry_pb2.Wrench(force=force_direction, torque=torque_direction)
    +    task_type = basic_command_pb2.ConstrainedManipulationCommand.Request.TASK_TYPE_R3_CIRCLE_FORCE
    +
    +    command = RobotCommandBuilder.constrained_manipulation_command(
    +        task_type=task_type, init_wrench_direction_in_frame_name=init_wrench_dir, force_limit=force_lim,
    +        torque_limit=torque_lim, tangential_speed=tangential_velocity, frame_name=frame_name)
    +    return command
    +
    +
    +def construct_drawer_task(velocity_normalized, force_limit=40):
    +    """ Helper function for opening/closing drawers
    +
    +    params:
    +    + velocity_normalized: normalized task tangential velocity in range [-1.0, 1.0]
    +    + force_limit (optional): positive value denoting max force robot will exert along task dimension
    +
    +    Output:
    +    + command: api command object
    +
    +    Notes:
    +    In this function, we assume the initial motion of the drawer is
    +    along the x axis of the hand (forward and backward). If the initial
    +    grasp is such that the initial motion needs to be something else,
    +    change the force direction.
    +    """
    +    velocity_limit = scale_velocity_lim_given_force_lim(force_limit)
    +    tangential_velocity = velocity_normalized * velocity_limit
    +    frame_name = "hand"
    +    force_lim = force_limit
    +    # Setting a placeholder value that doesn't matter, since we don't
    +    # apply a pure torque in this task.
    +    torque_lim = 5.0
    +    force_direction = geometry_pb2.Vec3(x=1.0, y=0.0, z=0.0)
    +    torque_direction = geometry_pb2.Vec3(x=0.0, y=0.0, z=0.0)
    +    init_wrench_dir = geometry_pb2.Wrench(force=force_direction, torque=torque_direction)
    +    task_type = basic_command_pb2.ConstrainedManipulationCommand.Request.TASK_TYPE_R3_LINEAR_FORCE
    +
    +    command = RobotCommandBuilder.constrained_manipulation_command(
    +        task_type=task_type, init_wrench_direction_in_frame_name=init_wrench_dir, force_limit=force_lim,
    +        torque_limit=torque_lim, tangential_speed=tangential_velocity, frame_name=frame_name)
    +    return command
    +
    +def construct_wheel_task(velocity_normalized, force_limit=40):
    +    """ Helper function for turning wheels while grasping the rim
    +    Use this when the wheel is grasped on the rim. If the grasp
    +    is on a handle that is free to rotate, use the crank task type.
    +    If the handle is not free to rotate, use this task type.
    +
    +    params:
    +    + velocity_normalized: normalized task tangential velocity in range [-1.0, 1.0]
    +    + force_limit (optional): positive value denoting max force robot will exert along task dimension
    +
    +    Output:
    +    + command: api command object
    +
    +    Notes:
    +    This assumes initial motion will be along the y axis of the hand,
    +    which is often the case. Change force_direction if that is not true.
    +    """
    +    velocity_limit = scale_velocity_lim_given_force_lim(force_limit)
    +    tangential_velocity = velocity_normalized * velocity_limit
    +    frame_name = "hand"
    +    force_lim = force_limit
    +    # Setting a placeholder value that doesn't matter, since we don't
    +    # apply a pure torque in this task.
    +    torque_lim = 5.0
    +    force_direction = geometry_pb2.Vec3(x=0.0, y=1.0, z=0.0)
    +    torque_direction = geometry_pb2.Vec3(x=0.0, y=0.0, z=0.0)
    +    init_wrench_dir = geometry_pb2.Wrench(force=force_direction, torque=torque_direction)
    +    task_type = basic_command_pb2.ConstrainedManipulationCommand.Request.TASK_TYPE_R3_CIRCLE_FORCE
    +
    +    command = RobotCommandBuilder.constrained_manipulation_command(
    +        task_type=task_type, init_wrench_direction_in_frame_name=init_wrench_dir, force_limit=force_lim,
    +        torque_limit=torque_lim, tangential_speed=tangential_velocity, frame_name=frame_name)
    +    return command
    +
    +
    +def construct_knob_task(rotational_velocity, torque_limit=5):
    +    """ Helper function for turning purely rotational knobs
    +    Use this for turning knobs/valves that do not have a lever arm
    +
    +    params:
    +    + rotational_velocity: commanded rotational velocity (does not have to be in [-1, 1])
    +    + torque_limit (optional): positive value denoting max torque robot will exert along axis of
    +                            rotation of the task
    +
    +    Output:
    +    + command: api command object
    +
    +    Notes:
    +    This assumes that the axis of rotation of of the knob is roughly parallel
    +    to the x axis of the hand. Change torque_direction if that is not the case.
    +    """
    +    frame_name = "hand"
    +    # Setting a placeholder value that doesn't matter, since we don't
    +    # apply a pure force in this task.
    +    force_lim = 40.0
    +    torque_lim = torque_limit
    +    force_direction = geometry_pb2.Vec3(x=0.0, y=0.0, z=0.0)
    +    torque_direction = geometry_pb2.Vec3(x=1.0, y=0.0, z=0.0)
    +    init_wrench_dir = geometry_pb2.Wrench(force=force_direction, torque=torque_direction)
    +    task_type = basic_command_pb2.ConstrainedManipulationCommand.Request.TASK_TYPE_SE3_ROTATIONAL_TORQUE
    +
    +    command = RobotCommandBuilder.constrained_manipulation_command(
    +        task_type=task_type, init_wrench_direction_in_frame_name=init_wrench_dir, force_limit=force_lim,
    +        torque_limit=torque_lim, rotational_speed=rotational_velocity, frame_name=frame_name)
    +    return command
    +
    +def construct_hold_pose_task():
    +    """ Helper function for holding the pose of the hand
    +    Use this if you want to hold the position of the hand,
    +    without leaving constrained manipulation.
    +
    +    Output:
    +    + command: api command object
    +    """
    +    frame_name = "hand"
    +    force_lim = 80
    +    torque_lim = 10
    +    force_direction = geometry_pb2.Vec3(x=1.0, y=0.0, z=0.0)
    +    torque_direction = geometry_pb2.Vec3(x=0.0, y=0.0, z=0.0)
    +    init_wrench_dir = geometry_pb2.Wrench(force=force_direction, torque=torque_direction)
    +    task_type = basic_command_pb2.ConstrainedManipulationCommand.Request.TASK_TYPE_HOLD_POSE
    +
    +    command = RobotCommandBuilder.constrained_manipulation_command(
    +        task_type=task_type, init_wrench_direction_in_frame_name=init_wrench_dir, force_limit=force_lim,
    +        torque_limit=torque_lim, tangential_speed=0.0, frame_name=frame_name)
    +    return command
    +
    +
    +# This function is used to scale the velocity limit given
    +# the force limit. This scaling ensures that when the measured arm 
    +# velocity is zero but desired velocity is max (vel_limit), we request
    +# max (force_limit) amount of force in that direction.
    +def scale_velocity_lim_given_force_lim(force_limit):
    +    internal_vel_tracking_gain = 7000.0/333.0
    +    vel_limit = force_limit / internal_vel_tracking_gain
    +    return vel_limit
    diff --git a/python/examples/arm_constrained_manipulation/requirements.txt b/python/examples/arm_constrained_manipulation/requirements.txt
    new file mode 100644
    index 000000000..177a49692
    --- /dev/null
    +++ b/python/examples/arm_constrained_manipulation/requirements.txt
    @@ -0,0 +1,2 @@
    +-f ../../../prebuilt
    +bosdyn-client >= 3.0
    \ No newline at end of file
    diff --git a/python/examples/arm_constrained_manipulation/run_constrained_manipulation.py b/python/examples/arm_constrained_manipulation/run_constrained_manipulation.py
    new file mode 100644
    index 000000000..136389800
    --- /dev/null
    +++ b/python/examples/arm_constrained_manipulation/run_constrained_manipulation.py
    @@ -0,0 +1,115 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""Test script to run constrained manipulation
    +"""
    +from __future__ import print_function
    +
    +import argparse
    +import sys
    +
    +import bosdyn.client
    +import bosdyn.client.lease
    +import bosdyn.client.util
    +from bosdyn.client import robot_command
    +from bosdyn.client.robot_command import RobotCommandClient, RobotCommandBuilder
    +from bosdyn.client.robot_state import RobotStateClient
    +
    +from constrained_manipulation_helper import*
    +import time
    +
    +
    +def run_constrained_manipulation(config):
    +    """A simple example of using the Boston Dynamics API to run a
    +       constrained manipulation task."""
    +
    +    print("Start doing constrained manipulation. Make sure Object of interest is grasped before starting.")
    +    # Build constrained manipulation command
    +    # You can build the task type of interest by using functions
    +    # defined in constrained_manipulation_helper.py
    +    # The input to this function is a normalized task velocity in range [-1, 1].
    +    # The normalized task velocity is scaled as a function of the force limit
    +    # (See the constrained_manipulation_helper.py for more details)
    +    # For heavier tasks, consider specifying the force or torque limit as well.
    +    if(config.task_type == 'crank'):
    +        command = construct_crank_task(config.task_velocity, force_limit=config.force_limit)
    +    elif(config.task_type == 'lever'):
    +        command = construct_lever_task(config.task_velocity, 
    +                    force_limit=config.force_limit, torque_limit=config.torque_limit)
    +    elif(config.task_type == 'left_handed_ballvalve'):
    +        command = construct_left_handed_ballvalve_task(config.task_velocity, 
    +                    force_limit=config.force_limit, torque_limit=config.torque_limit)
    +    elif(config.task_type == 'right_handed_ballvalve'):
    +        command = construct_right_handed_ballvalve_task(config.task_velocity, 
    +                    force_limit=config.force_limit, torque_limit=config.torque_limit)
    +    elif(config.task_type == 'cabinet'):
    +        command = construct_cabinet_task(config.task_velocity, force_limit=config.force_limit)
    +    elif(config.task_type == 'wheel'):
    +        command = construct_wheel_task(config.task_velocity, force_limit=config.force_limit)
    +    elif(config.task_type == 'drawer'):
    +        command = construct_drawer_task(config.task_velocity, force_limit=config.force_limit)
    +    elif(config.task_type == 'knob'):
    +        command = construct_knob_task(config.task_velocity, torque_limit=config.torque_limit)
    +    else:
    +        print("Unspecified task type. Exit.")
    +        return
    +
    +    bosdyn.client.util.setup_logging(config.verbose)
    +    sdk = bosdyn.client.create_standard_sdk('ConstrainedManipulationClient')
    +
    +    robot = sdk.create_robot(config.hostname)
    +    robot.authenticate(config.username, config.password)
    +    robot.time_sync.wait_for_sync()
    +
    +    lease_client = robot.ensure_client(
    +        bosdyn.client.lease.LeaseClient.default_service_name)
    +
    +    # Check to see if robot is powered on.
    +    assert robot.is_powered_on(), "Robot must be powered on."
    +    robot.logger.info("Robot powered on.")
    +    # Check if the gripper is already holding the object.
    +    robot_state_client = robot.ensure_client(RobotStateClient.default_service_name)
    +    is_gripper_holding = robot_state_client.get_robot_state().manipulator_state.is_gripper_holding_item
    +    assert is_gripper_holding, "Gripper is not holding the object. If it is, override the gripper state holding."
    +
    +    # Note that the take lease API is used, rather than acquire. Using acquire is typically a
    +    # better practice, but in this example, a user might want to switch back and forth between
    +    # using the tablet and using this script. Using take allows for directly hijacking control
    +    # away from the tablet.
    +    lease = lease_client.take()
    +
    +    command_client = robot.ensure_client(
    +        robot_command.RobotCommandClient.default_service_name)
    +    try:
    +        with bosdyn.client.lease.LeaseKeepAlive(lease_client):
    +            command.full_body_command.constrained_manipulation_request.end_time.CopyFrom(
    +                robot.time_sync.robot_timestamp_from_local_secs(time.time() + 10))
    +            command_client.robot_command_async(command)
    +            time.sleep(15.0)
    +
    +    finally:
    +        lease_client.return_lease(lease)
    +
    +
    +def main(argv):
    +    """Command line interface."""
    +    parser = argparse.ArgumentParser()
    +    bosdyn.client.util.add_common_arguments(parser)
    +    parser.add_argument('--task-type', help='Specify the task type to manipulate.', default='crank', 
    +                        choices=['lever', 'left_handed_ballvalve', 'right_handed_ballvalve', 'crank', 'wheel', 'cabinet', 'drawer', 'knob'])
    +    parser.add_argument('--task-velocity',
    +                        help='Desired task velocity', type=float, default=0.5)
    +    parser.add_argument('--force-limit',
    +                        help='Max force to be applied along task dimensions', type=float, default=40)
    +    parser.add_argument('--torque-limit',
    +                        help='Max force to be applied along task dimensions', type=float, default=5.0)
    +    options = parser.parse_args(argv)
    +    run_constrained_manipulation(options)
    +
    +
    +if __name__ == '__main__':
    +    if not main(sys.argv[1:]):
    +        sys.exit(1)
    diff --git a/python/examples/arm_door/requirements.txt b/python/examples/arm_door/requirements.txt
    index 8a7a93f59..2a31029c7 100644
    --- a/python/examples/arm_door/requirements.txt
    +++ b/python/examples/arm_door/requirements.txt
    @@ -1,5 +1,5 @@
     -f ../../../prebuilt
    -bosdyn-client ~= 2.3
    +bosdyn-client >= 2.3
     
     numpy==1.19.3
     opencv-python >= 3.4.2.17
    \ No newline at end of file
    diff --git a/python/examples/arm_force_control/force_wrench_control.py b/python/examples/arm_force_control/force_wrench_control.py
    index 8be21ed08..f60902de0 100644
    --- a/python/examples/arm_force_control/force_wrench_control.py
    +++ b/python/examples/arm_force_control/force_wrench_control.py
    @@ -71,7 +71,9 @@ def force_wrench(config):
                 command_client = robot.ensure_client(RobotCommandClient.default_service_name)
                 blocking_stand(command_client, timeout_sec=10)
                 robot.logger.info("Robot standing.")
    -
    +            # Send a second stand command with a lowered body height to allow the arm to reach the ground.
    +            stand_command = RobotCommandBuilder.synchro_stand_command(body_height=-0.15)
    +            command_id = command_client.robot_command(stand_command, timeout=2)
                 time.sleep(2.0)
     
                 # Unstow the arm
    @@ -125,8 +127,8 @@ def force_wrench(config):
                 # Hand will start to the left and move to the right.
     
                 hand_x = 0.75  # in front of the robot.
    -            hand_y_start = 0  # centered
    -            hand_y_end = -0.5  # to the right
    +            hand_y_start = 0.25  # to the left
    +            hand_y_end = -0.25  # to the right
                 hand_z = 0  # will be ignored since we'll have a force in the Z axis.
     
                 f_z = -5  # Newtons
    diff --git a/python/examples/arm_force_control/requirements.txt b/python/examples/arm_force_control/requirements.txt
    index 5f0983252..ea085f145 100644
    --- a/python/examples/arm_force_control/requirements.txt
    +++ b/python/examples/arm_force_control/requirements.txt
    @@ -1,2 +1,2 @@
     -f ../../../prebuilt
    -bosdyn-client ~= 2.3
    +bosdyn-client >= 2.3
    diff --git a/python/examples/arm_gaze/arm_gaze.py b/python/examples/arm_gaze/arm_gaze.py
    index c4d26f450..36788c3de 100644
    --- a/python/examples/arm_gaze/arm_gaze.py
    +++ b/python/examples/arm_gaze/arm_gaze.py
    @@ -14,7 +14,7 @@
     import bosdyn.client.lease
     import bosdyn.client.util
     
    -from bosdyn.client.robot_command import RobotCommandClient, RobotCommandBuilder, blocking_stand
    +from bosdyn.client.robot_command import RobotCommandClient, RobotCommandBuilder, blocking_stand, block_until_arm_arrives
     from bosdyn.client.frame_helpers import *
     from bosdyn.client.robot_state import RobotStateClient
     from bosdyn.client import math_helpers
    @@ -158,10 +158,11 @@ def gaze_control(config):
                 synchro_command = RobotCommandBuilder.build_synchro_command(gripper_command, command)
     
                 # Send the request
    -            command_client.robot_command(command)
    +            gaze_cmd_id = command_client.robot_command(command)
                 robot.logger.info('Sending gaze trajectory.')
     
    -            time.sleep(traj_time + 3.0)
    +            # Wait until the robot completes the gaze before issuing the next command.
    +            block_until_arm_arrives(command_client, gaze_cmd_id, timeout_sec=traj_time+3.0)
     
                 # ------------- #
     
    @@ -222,10 +223,11 @@ def gaze_control(config):
                 synchro_command = RobotCommandBuilder.build_synchro_command(gripper_command, command)
     
                 # Send the request
    -            command_client.robot_command(synchro_command)
    +            gaze_cmd_id = command_client.robot_command(synchro_command)
                 robot.logger.info('Sending gaze trajectory with hand movement.')
     
    -            time.sleep(traj_time + 3.0)
    +            # Wait until the robot completes the gaze before powering off.
    +            block_until_arm_arrives(command_client, gaze_cmd_id, timeout_sec=traj_time+3.0)
     
                 # Power the robot off. By specifying "cut_immediately=False", a safe power off command
                 # is issued to the robot. This will attempt to sit the robot before powering off.
    diff --git a/python/examples/arm_gaze/requirements.txt b/python/examples/arm_gaze/requirements.txt
    index 5f0983252..ea085f145 100644
    --- a/python/examples/arm_gaze/requirements.txt
    +++ b/python/examples/arm_gaze/requirements.txt
    @@ -1,2 +1,2 @@
     -f ../../../prebuilt
    -bosdyn-client ~= 2.3
    +bosdyn-client >= 2.3
    diff --git a/python/examples/arm_gcode/gcode.py b/python/examples/arm_gcode/gcode.py
    index aaff708b0..2d38c8d21 100644
    --- a/python/examples/arm_gcode/gcode.py
    +++ b/python/examples/arm_gcode/gcode.py
    @@ -497,14 +497,14 @@ def get_transforms(use_vision_frame, robot_state):
             world_T_body = get_a_tform_b(robot_state.kinematic_state.transforms_snapshot, "vision",
                                          "body")
     
    -    body_T_hand = get_a_tform_b(robot_state.kinematic_state.transforms_snapshot, "body",
    -                                "hand")
    +    body_T_hand = get_a_tform_b(robot_state.kinematic_state.transforms_snapshot, "body", "hand")
         world_T_hand = world_T_body * body_T_hand
     
         odom_T_body = get_a_tform_b(robot_state.kinematic_state.transforms_snapshot, "odom", "body")
     
         return (world_T_body, body_T_hand, world_T_hand, odom_T_body)
     
    +
     def do_pause():
         input('Paused, press enter to continue...')
     
    @@ -513,7 +513,7 @@ def run_gcode_program(config):
         """A simple example of using the Boston Dynamics API to command a Spot robot."""
     
         config_parser = configparser.ConfigParser()
    -    config_parser.readfp(open('gcode.cfg'))
    +    config_parser.read_file(open('gcode.cfg'))
         gcode_file = config_parser.get("General", "gcode_file")
         scale = config_parser.getfloat("General", "scale")
         min_dist_to_goal = config_parser.getfloat("General", "min_dist_to_goal")
    @@ -650,10 +650,12 @@ def run_gcode_program(config):
                 # Send the request
                 odom_T_hand_obj = odom_T_hand.to_proto()
     
    +            move_time = 0.000001  # move as fast as possible because we will use (default) velocity/accel limiting.
     
    -            move_time = 0.000001 # move as fast as possible because we will use (default) velocity/accel limiting.
    -
    -            arm_command = RobotCommandBuilder.arm_pose_command(odom_T_hand_obj.position.x, odom_T_hand_obj.position.y, odom_T_hand_obj.position.z, odom_T_hand_obj.rotation.w, odom_T_hand_obj.rotation.x, odom_T_hand_obj.rotation.y, odom_T_hand_obj.rotation.z, ODOM_FRAME_NAME, move_time)
    +            arm_command = RobotCommandBuilder.arm_pose_command(
    +                odom_T_hand_obj.position.x, odom_T_hand_obj.position.y, odom_T_hand_obj.position.z,
    +                odom_T_hand_obj.rotation.w, odom_T_hand_obj.rotation.x, odom_T_hand_obj.rotation.y,
    +                odom_T_hand_obj.rotation.z, ODOM_FRAME_NAME, move_time)
     
                 command = RobotCommandBuilder.build_synchro_command(arm_command)
     
    @@ -692,9 +694,9 @@ def run_gcode_program(config):
                                                                     rotation=q_wall_proto)
     
                 # Touch the ground/wall
    -            move_arm(robot_state, True, [world_T_hand],
    -                     arm_surface_contact_client, velocity, allow_walking, world_T_admittance_frame,
    -                     press_force_percent, api_send_frame, use_xy_to_z_cross_term, bias_force_x)
    +            move_arm(robot_state, True, [world_T_hand], arm_surface_contact_client, velocity,
    +                     allow_walking, world_T_admittance_frame, press_force_percent, api_send_frame,
    +                     use_xy_to_z_cross_term, bias_force_x)
     
                 time.sleep(4.0)
                 last_admittance = True
    @@ -707,10 +709,11 @@ def run_gcode_program(config):
                     use_vision_frame, robot_state)
     
                 odom_T_ground_plane = get_a_tform_b(robot_state.kinematic_state.transforms_snapshot,
    -                                               "odom", "gpe")
    +                                                "odom", "gpe")
                 world_T_odom = world_T_body * odom_T_body.inverse()
     
    -            (gx, gy, gz) = world_T_odom.transform_point(odom_T_ground_plane.x, odom_T_ground_plane.y,
    +            (gx, gy, gz) = world_T_odom.transform_point(odom_T_ground_plane.x,
    +                                                        odom_T_ground_plane.y,
                                                             odom_T_ground_plane.z)
                 ground_plane_rt_vo = [gx, gy, gz]
     
    @@ -741,20 +744,21 @@ def run_gcode_program(config):
                 gcode.set_origin(world_T_origin, world_T_admittance_frame)
                 robot.logger.info('Origin set')
     
    -            (is_admittance, world_T_goals, is_pause) = gcode.get_next_world_T_goals(ground_plane_rt_vo)
    +            (is_admittance, world_T_goals,
    +             is_pause) = gcode.get_next_world_T_goals(ground_plane_rt_vo)
     
                 while is_pause:
                     do_pause()
    -                (is_admittance, world_T_goals, is_pause) = gcode.get_next_world_T_goals(ground_plane_rt_vo)
    +                (is_admittance, world_T_goals,
    +                 is_pause) = gcode.get_next_world_T_goals(ground_plane_rt_vo)
     
                 if world_T_goals is None:
                     # we're done!
                     done = True
     
    -            move_arm(robot_state, is_admittance,
    -                     world_T_goals, arm_surface_contact_client, velocity, allow_walking,
    -                     world_T_admittance_frame, press_force_percent, api_send_frame,
    -                     use_xy_to_z_cross_term, bias_force_x)
    +            move_arm(robot_state, is_admittance, world_T_goals, arm_surface_contact_client,
    +                     velocity, allow_walking, world_T_admittance_frame, press_force_percent,
    +                     api_send_frame, use_xy_to_z_cross_term, bias_force_x)
                 odom_T_hand_goal = world_T_odom.inverse() * world_T_goals[-1]
                 last_admittance = is_admittance
     
    @@ -794,12 +798,13 @@ def run_gcode_program(config):
     
                     if arm_near_goal:
                         # Compute where to go.
    -                    (is_admittance,
    -                     world_T_goals, is_pause) = gcode.get_next_world_T_goals(ground_plane_rt_vo)
    +                    (is_admittance, world_T_goals,
    +                     is_pause) = gcode.get_next_world_T_goals(ground_plane_rt_vo)
     
                         while is_pause:
                             do_pause()
    -                        (is_admittance, world_T_goals, is_pause) = gcode.get_next_world_T_goals(ground_plane_rt_vo)
    +                        (is_admittance, world_T_goals,
    +                         is_pause) = gcode.get_next_world_T_goals(ground_plane_rt_vo)
     
                         if world_T_goals is None:
                             # we're done!
    @@ -807,10 +812,9 @@ def run_gcode_program(config):
                             robot.logger.info("Gcode program finished.")
                             break
     
    -                    move_arm(robot_state, is_admittance,
    -                             world_T_goals, arm_surface_contact_client, velocity, allow_walking,
    -                             world_T_admittance_frame, press_force_percent, api_send_frame,
    -                             use_xy_to_z_cross_term, bias_force_x)
    +                    move_arm(robot_state, is_admittance, world_T_goals, arm_surface_contact_client,
    +                             velocity, allow_walking, world_T_admittance_frame, press_force_percent,
    +                             api_send_frame, use_xy_to_z_cross_term, bias_force_x)
                         odom_T_hand_goal = world_T_odom.inverse() * world_T_goals[-1]
     
                         if is_admittance != last_admittance:
    @@ -848,8 +852,7 @@ def run_gcode_program(config):
                         goal_heading=odom_T_walk_se2.angle, frame_name="odom")
                     end_time = 15.0
                     #Issue the command to the robot
    -                command_client.robot_command(command=walk_cmd,
    -                    end_time_secs=time.time() + end_time)
    +                command_client.robot_command(command=walk_cmd, end_time_secs=time.time() + end_time)
                     time.sleep(end_time)
     
                 robot.logger.info('Done.')
    diff --git a/python/examples/arm_gcode/requirements.txt b/python/examples/arm_gcode/requirements.txt
    index 4b0a45bae..61739869a 100644
    --- a/python/examples/arm_gcode/requirements.txt
    +++ b/python/examples/arm_gcode/requirements.txt
    @@ -1,4 +1,4 @@
     -f ../../../prebuilt
    -bosdyn-client ~= 2.3
    +bosdyn-client >= 2.3
     
     numpy >= 1.16.4
    diff --git a/python/examples/arm_grasp/requirements.txt b/python/examples/arm_grasp/requirements.txt
    index 029684b2a..4f39d4137 100644
    --- a/python/examples/arm_grasp/requirements.txt
    +++ b/python/examples/arm_grasp/requirements.txt
    @@ -1,5 +1,5 @@
     -f ../../../prebuilt
    -bosdyn-client ~= 2.3
    +bosdyn-client >= 2.3
     
     opencv-python >= 3.4.2.17
     numpy >= 1.16.4
    diff --git a/python/examples/arm_joint_move/arm_joint_move.py b/python/examples/arm_joint_move/arm_joint_move.py
    index f1d7de18b..f3bbb5ea1 100644
    --- a/python/examples/arm_joint_move/arm_joint_move.py
    +++ b/python/examples/arm_joint_move/arm_joint_move.py
    @@ -15,7 +15,7 @@
     import bosdyn.client.lease
     import bosdyn.client.util
     
    -from bosdyn.client.robot_command import RobotCommandClient, RobotCommandBuilder, blocking_stand
    +from bosdyn.client.robot_command import RobotCommandClient, RobotCommandBuilder, blocking_stand, block_until_arm_arrives
     from bosdyn.client.robot_state import RobotStateClient
     from bosdyn.client.estop import EstopClient
     from bosdyn.client import math_helpers
    @@ -27,7 +27,7 @@
     from google.protobuf import wrappers_pb2
     from bosdyn.client.frame_helpers import *
     from bosdyn.api import geometry_pb2
    -from bosdyn.util import seconds_to_duration
    +from bosdyn.util import duration_to_seconds
     
     import traceback
     import time
    @@ -44,12 +44,37 @@ def verify_estop(robot):
             raise Exception(error_message)
     
     
    -def to_double_val(val):
    -    return wrappers_pb2.DoubleValue(value=val)
    +def make_robot_command(arm_joint_traj):
    +    """ Helper function to create a RobotCommand from an ArmJointTrajectory. 
    +        The returned command will be a SynchronizedCommand with an ArmJointMoveCommand
    +        filled out to follow the passed in trajectory. """
    +
    +    joint_move_command = arm_command_pb2.ArmJointMoveCommand.Request(trajectory=arm_joint_traj)
    +    arm_command = arm_command_pb2.ArmCommand.Request(arm_joint_move_command=joint_move_command)
    +    sync_arm = synchronized_command_pb2.SynchronizedCommand.Request(arm_command=arm_command)
    +    arm_sync_robot_cmd = robot_command_pb2.RobotCommand(synchronized_command=sync_arm)
    +    return RobotCommandBuilder.build_synchro_command(arm_sync_robot_cmd)
    +
    +
    +def print_feedback(feedback_resp, logger):
    +    """ Helper function to query for ArmJointMove feedback, and print it to the console.
    +        Returns the time_to_goal value reported in the feedback """
    +    joint_move_feedback = feedback_resp.feedback.synchronized_feedback.arm_command_feedback.arm_joint_move_feedback
    +    logger.info(f'  planner_status = {joint_move_feedback.planner_status}')
    +    logger.info(
    +        f'  time_to_goal = {duration_to_seconds(joint_move_feedback.time_to_goal):.2f} seconds.')
    +
    +    # Query planned_points to determine target pose of arm
    +    logger.info('  planned_points:')
    +    for idx, points in enumerate(joint_move_feedback.planned_points):
    +        pos = points.position
    +        pos_str = f'sh0 = {pos.sh0.value:.3f}, sh1 = {pos.sh1.value:.3f}, el0 = {pos.el0.value:.3f}, el1 = {pos.el1.value:.3f}, wr0 = {pos.wr0.value:.3f}, wr1 = {pos.wr1.value:.3f}'
    +        logger.info(f'    {idx}: {pos_str}')
    +    return duration_to_seconds(joint_move_feedback.time_to_goal)
     
     
     def joint_move_example(config):
    -    """A simple example of using the Boston Dynamics API to command Spot's arm."""
    +    """A simple example of using the Boston Dynamics API to command Spot's arm to perform joint moves."""
     
         # See hello_spot.py for an explanation of these lines.
         bosdyn.client.util.setup_logging(config.verbose)
    @@ -90,38 +115,86 @@ def joint_move_example(config):
     
                 time.sleep(2.0)
     
    -            # Do a joint move to move the arm around.
    -            sh0 = wrappers_pb2.DoubleValue(value=0.0692)
    -            sh1 = wrappers_pb2.DoubleValue(value=-1.882)
    -            el0 = wrappers_pb2.DoubleValue(value=1.652)
    -            el1 = wrappers_pb2.DoubleValue(value=-0.0691)
    -            wr0 = wrappers_pb2.DoubleValue(value=1.622)
    -            wr1 = wrappers_pb2.DoubleValue(value=1.550)
    +            # Example 1: issue a single point trajectory without a time_since_reference in order to perform
    +            # a minimum time joint move to the goal obeying the default acceleration and velocity limits.
    +            sh0 = 0.0692
    +            sh1 = -1.882
    +            el0 = 1.652
    +            el1 = -0.0691
    +            wr0 = 1.622
    +            wr1 = 1.550
    +
    +            traj_point = RobotCommandBuilder.create_arm_joint_trajectory_point(
    +                sh0, sh1, el0, el1, wr0, wr1)
    +            arm_joint_traj = arm_command_pb2.ArmJointTrajectory(points=[traj_point])
    +            # Make a RobotCommand
    +            command = make_robot_command(arm_joint_traj)
     
    -            # Build up a proto.
    -            joint_position1 = arm_command_pb2.ArmJointPosition(sh0=sh0, sh1=sh1, el0=el0, el1=el1,
    -                                                               wr0=wr0, wr1=wr1)
    +            # Send the request
    +            cmd_id = command_client.robot_command(command)
    +            robot.logger.info('Moving arm to position 1.')
     
    -            traj_point = arm_command_pb2.ArmJointTrajectoryPoint(position=joint_position1)
    -            arm_joint_traj = arm_command_pb2.ArmJointTrajectory(points=[traj_point])
    -            joint_move_command = arm_command_pb2.ArmJointMoveCommand.Request(
    -                trajectory=arm_joint_traj)
    -            arm_command = arm_command_pb2.ArmCommand.Request(
    -                arm_joint_move_command=joint_move_command)
    -            sync_arm = synchronized_command_pb2.SynchronizedCommand.Request(arm_command=arm_command)
    -            arm_sync_robot_cmd = robot_command_pb2.RobotCommand(synchronized_command=sync_arm)
    +            # Query for feedback to determine how long the goto will take.
    +            feedback_resp = command_client.robot_command_feedback(cmd_id)
    +            robot.logger.info("Feedback for Example 1: single point goto")
    +            time_to_goal = print_feedback(feedback_resp, robot.logger)
    +            time.sleep(time_to_goal)
    +
    +            # Example 2: Single point trajectory with maximum acceleration/velocity constraints specified such
    +            # that the solver has to modify the desired points to honor the constraints
    +            sh0 = 0.0
    +            sh1 = -2.0
    +            el0 = 2.6
    +            el1 = 0.0
    +            wr0 = -0.6
    +            wr1 = 0.0
    +            max_vel = wrappers_pb2.DoubleValue(value=1)
    +            max_acc = wrappers_pb2.DoubleValue(value=5)
    +            traj_point = RobotCommandBuilder.create_arm_joint_trajectory_point(
    +                sh0, sh1, el0, el1, wr0, wr1, time_since_reference_secs=1.5)
    +            arm_joint_traj = arm_command_pb2.ArmJointTrajectory(points=[traj_point],
    +                                                                maximum_velocity=max_vel,
    +                                                                maximum_acceleration=max_acc)
    +            # Make a RobotCommand
    +            command = make_robot_command(arm_joint_traj)
     
    -            # Make the open gripper RobotCommand
    -            gripper_command = RobotCommandBuilder.claw_gripper_open_fraction_command(1.0)
    +            # Send the request
    +            cmd_id = command_client.robot_command(command)
    +            robot.logger.info(
    +                'Requesting a single point trajectory with unsatisfiable constraints.')
    +
    +            # Query for feedback
    +            feedback_resp = command_client.robot_command_feedback(cmd_id)
    +            robot.logger.info("Feedback for Example 2: planner modifies trajectory")
    +            time_to_goal = print_feedback(feedback_resp, robot.logger)
    +            time.sleep(time_to_goal)
    +
    +            # Example 3: Single point trajectory with default acceleration/velocity constraints and
    +            # time_since_reference_secs large enough such that the solver can plan a solution to the
    +            # points that also satisfies the constraints.
    +            sh0 = 0.0692
    +            sh1 = -1.882
    +            el0 = 1.652
    +            el1 = -0.0691
    +            wr0 = 1.622
    +            wr1 = 1.550
    +            traj_point = RobotCommandBuilder.create_arm_joint_trajectory_point(
    +                sh0, sh1, el0, el1, wr0, wr1, time_since_reference_secs=1.5)
    +
    +            arm_joint_traj = arm_command_pb2.ArmJointTrajectory(points=[traj_point])
     
    -            # Combine the arm and gripper commands into one RobotCommand
    -            command = RobotCommandBuilder.build_synchro_command(gripper_command, arm_sync_robot_cmd)
    +            # Make a RobotCommand
    +            command = make_robot_command(arm_joint_traj)
     
                 # Send the request
    -            command_client.robot_command(command)
    -            robot.logger.info('Moving arm to position 1.')
    +            cmd_id = command_client.robot_command(command)
    +            robot.logger.info('Requesting a single point trajectory with satisfiable constraints.')
     
    -            time.sleep(4.0)
    +            # Query for feedback
    +            feedback_resp = command_client.robot_command_feedback(cmd_id)
    +            robot.logger.info("Feedback for Example 3: unmodified trajectory")
    +            time_to_goal = print_feedback(feedback_resp, robot.logger)
    +            time.sleep(time_to_goal)
     
                 # ----- Do a two-point joint move trajectory ------
     
    @@ -136,30 +209,34 @@ def joint_move_example(config):
                 time.sleep(2.0)
     
                 # First point position
    -            sh0 = wrappers_pb2.DoubleValue(value=-1.5)
    -            sh1 = wrappers_pb2.DoubleValue(value=-0.8)
    -            el0 = wrappers_pb2.DoubleValue(value=1.7)
    -            el1 = wrappers_pb2.DoubleValue(value=0.0)
    -            wr0 = wrappers_pb2.DoubleValue(value=0.5)
    -            wr1 = wrappers_pb2.DoubleValue(value=0.0)
    +            sh0 = -1.5
    +            sh1 = -0.8
    +            el0 = 1.7
    +            el1 = 0.0
    +            wr0 = 0.5
    +            wr1 = 0.0
    +
    +            # First point time (seconds)
    +            first_point_t = 2.0
    +
    +            # Build the proto for the trajectory point.
    +            traj_point1 = RobotCommandBuilder.create_arm_joint_trajectory_point(
    +                sh0, sh1, el0, el1, wr0, wr1, first_point_t)
     
    -            # Build up a proto.
    -            joint_position1 = arm_command_pb2.ArmJointPosition(sh0=sh0, sh1=sh1, el0=el0, el1=el1,
    -                                                               wr0=wr0, wr1=wr1)
                 # Second point position
    -            sh0 = wrappers_pb2.DoubleValue(value=1.0)
    -            sh1 = wrappers_pb2.DoubleValue(value=-0.2)
    -            el0 = wrappers_pb2.DoubleValue(value=1.3)
    -            el1 = wrappers_pb2.DoubleValue(value=-1.3)
    -            wr0 = wrappers_pb2.DoubleValue(value=-1.5)
    -            wr1 = wrappers_pb2.DoubleValue(value=1.5)
    +            sh0 = 1.0
    +            sh1 = -0.2
    +            el0 = 1.3
    +            el1 = -1.3
    +            wr0 = -1.5
    +            wr1 = 1.5
     
                 # Second point time (seconds)
    -            first_point_t = 2.0
                 second_point_t = 4.0
     
    -            joint_position2 = arm_command_pb2.ArmJointPosition(sh0=sh0, sh1=sh1, el0=el0, el1=el1,
    -                                                               wr0=wr0, wr1=wr1)
    +            # Build the proto for the second trajectory point.
    +            traj_point2 = RobotCommandBuilder.create_arm_joint_trajectory_point(
    +                sh0, sh1, el0, el1, wr0, wr1, second_point_t)
     
                 # Optionally, set the maximum allowable velocity in rad/s that a joint is allowed to
                 # travel at. Also set the maximum allowable acceleration in rad/s^2 that a joint is
    @@ -174,30 +251,23 @@ def joint_move_example(config):
                 max_acc = wrappers_pb2.DoubleValue(value=15)
     
                 # Build up a proto.
    -            traj_point1 = arm_command_pb2.ArmJointTrajectoryPoint(
    -                position=joint_position1, time_since_reference=seconds_to_duration(first_point_t))
    -            traj_point2 = arm_command_pb2.ArmJointTrajectoryPoint(
    -                position=joint_position2, time_since_reference=seconds_to_duration(second_point_t))
    -            arm_joint_traj = arm_command_pb2.ArmJointTrajectory(
    -                points=[traj_point1, traj_point2], maximum_velocity=max_vel, maximum_acceleration=max_acc)
    -            joint_move_command = arm_command_pb2.ArmJointMoveCommand.Request(
    -                trajectory=arm_joint_traj)
    -            arm_command = arm_command_pb2.ArmCommand.Request(
    -                arm_joint_move_command=joint_move_command)
    -            sync_arm = synchronized_command_pb2.SynchronizedCommand.Request(arm_command=arm_command)
    -            arm_sync_robot_cmd = robot_command_pb2.RobotCommand(synchronized_command=sync_arm)
    -
    -            # Make the open gripper RobotCommand
    -            gripper_command = RobotCommandBuilder.claw_gripper_open_fraction_command(1.0)
    -
    -            # Combine the arm and gripper commands into one RobotCommand
    -            command = RobotCommandBuilder.build_synchro_command(gripper_command, arm_sync_robot_cmd)
    +            arm_joint_traj = arm_command_pb2.ArmJointTrajectory(points=[traj_point1, traj_point2],
    +                                                                maximum_velocity=max_vel,
    +                                                                maximum_acceleration=max_acc)
    +            # Make a RobotCommand
    +            command = make_robot_command(arm_joint_traj)
     
                 # Send the request
    -            command_client.robot_command(command)
    +            cmd_id = command_client.robot_command(command)
                 robot.logger.info('Moving arm along 2-point joint trajectory.')
     
    -            time.sleep(5.0)
    +            # Query for feedback to determine exactly what the planned trajectory is.
    +            feedback_resp = command_client.robot_command_feedback(cmd_id)
    +            robot.logger.info("Feedback for 2-point joint trajectory")
    +            print_feedback(feedback_resp, robot.logger)
    +
    +            # Wait until the move completes before powering off.
    +            block_until_arm_arrives(command_client, cmd_id, second_point_t + 3.0)
     
                 # Power the robot off. By specifying "cut_immediately=False", a safe power off command
                 # is issued to the robot. This will attempt to sit the robot before powering off.
    diff --git a/python/examples/arm_joint_move/requirements.txt b/python/examples/arm_joint_move/requirements.txt
    index 5f0983252..ea085f145 100644
    --- a/python/examples/arm_joint_move/requirements.txt
    +++ b/python/examples/arm_joint_move/requirements.txt
    @@ -1,2 +1,2 @@
     -f ../../../prebuilt
    -bosdyn-client ~= 2.3
    +bosdyn-client >= 2.3
    diff --git a/python/examples/arm_simple/arm_simple.py b/python/examples/arm_simple/arm_simple.py
    index dd71631e7..000a2a6fe 100644
    --- a/python/examples/arm_simple/arm_simple.py
    +++ b/python/examples/arm_simple/arm_simple.py
    @@ -156,6 +156,7 @@ def hello_arm(config):
             # If we successfully acquired a lease, return it.
             lease_client.return_lease(lease)
     
    +
     def block_until_arm_arrives_with_prints(robot, command_client, cmd_id):
         """Block until the arm arrives at the goal and print the distance remaining.
             Note: a version of this function is available as a helper in robot_command
    @@ -163,13 +164,20 @@ def block_until_arm_arrives_with_prints(robot, command_client, cmd_id):
         """
         while True:
             feedback_resp = command_client.robot_command_feedback(cmd_id)
    -        robot.logger.info('Distance to go: ' + '{:.2f} meters'.format(feedback_resp.feedback.synchronized_feedback.arm_command_feedback.arm_cartesian_feedback.measured_pos_distance_to_goal) + ', {:.2f} radians'.format(feedback_resp.feedback.synchronized_feedback.arm_command_feedback.arm_cartesian_feedback.measured_rot_distance_to_goal))
    +        robot.logger.info(
    +            'Distance to go: ' +
    +            '{:.2f} meters'.format(feedback_resp.feedback.synchronized_feedback.arm_command_feedback
    +                                   .arm_cartesian_feedback.measured_pos_distance_to_goal) +
    +            ', {:.2f} radians'.format(
    +                feedback_resp.feedback.synchronized_feedback.arm_command_feedback.
    +                arm_cartesian_feedback.measured_rot_distance_to_goal))
     
             if feedback_resp.feedback.synchronized_feedback.arm_command_feedback.arm_cartesian_feedback.status == arm_command_pb2.ArmCartesianCommand.Feedback.STATUS_TRAJECTORY_COMPLETE:
                 robot.logger.info('Move complete.')
                 break
             time.sleep(0.1)
     
    +
     def main(argv):
         """Command line interface."""
         parser = argparse.ArgumentParser()
    diff --git a/python/examples/arm_simple/requirements.txt b/python/examples/arm_simple/requirements.txt
    index 5f0983252..ea085f145 100644
    --- a/python/examples/arm_simple/requirements.txt
    +++ b/python/examples/arm_simple/requirements.txt
    @@ -1,2 +1,2 @@
     -f ../../../prebuilt
    -bosdyn-client ~= 2.3
    +bosdyn-client >= 2.3
    diff --git a/python/examples/arm_stow_unstow/requirements.txt b/python/examples/arm_stow_unstow/requirements.txt
    index 5f0983252..ea085f145 100644
    --- a/python/examples/arm_stow_unstow/requirements.txt
    +++ b/python/examples/arm_stow_unstow/requirements.txt
    @@ -1,2 +1,2 @@
     -f ../../../prebuilt
    -bosdyn-client ~= 2.3
    +bosdyn-client >= 2.3
    diff --git a/python/examples/arm_surface_contact/arm_surface_contact.py b/python/examples/arm_surface_contact/arm_surface_contact.py
    index add7a3583..e6342a031 100644
    --- a/python/examples/arm_surface_contact/arm_surface_contact.py
    +++ b/python/examples/arm_surface_contact/arm_surface_contact.py
    @@ -99,7 +99,7 @@ def arm_surface_contact(config):
                 hand_z = 0  # will be ignored since we'll have a force in the Z axis.
     
                 f_z = -0.05  # percentage of maximum press force, negative to press down
    -                         # be careful setting this too large, you can knock the robot over
    +            # be careful setting this too large, you can knock the robot over
                 percentage_press = geometry_pb2.Vec3(x=0, y=0, z=f_z)
     
                 hand_vec3_start_rt_body = geometry_pb2.Vec3(x=hand_x, y=hand_y_start, z=hand_z)
    @@ -140,15 +140,18 @@ def arm_surface_contact(config):
                 gripper_command = gripper_cmd_packed.synchronized_command.gripper_command.claw_gripper_command
     
                 cmd = arm_surface_contact_pb2.ArmSurfaceContact.Request(
    -                pose_trajectory_in_task=hand_traj, root_frame_name=ODOM_FRAME_NAME,
    +                pose_trajectory_in_task=hand_traj,
    +                root_frame_name=ODOM_FRAME_NAME,
                     press_force_percentage=percentage_press,
                     x_axis=arm_surface_contact_pb2.ArmSurfaceContact.Request.AXIS_MODE_POSITION,
                     y_axis=arm_surface_contact_pb2.ArmSurfaceContact.Request.AXIS_MODE_POSITION,
                     z_axis=arm_surface_contact_pb2.ArmSurfaceContact.Request.AXIS_MODE_FORCE,
    -                z_admittance=arm_surface_contact_pb2.ArmSurfaceContact.Request.ADMITTANCE_SETTING_LOOSE,
    +                z_admittance=arm_surface_contact_pb2.ArmSurfaceContact.Request.
    +                ADMITTANCE_SETTING_LOOSE,
                     # Enable the cross term so that if the arm gets stuck in a rut, it will retract
                     # upwards slightly, preventing excessive lateral forces.
    -                xy_to_z_cross_term_admittance=arm_surface_contact_pb2.ArmSurfaceContact.Request.ADMITTANCE_SETTING_VERY_STIFF,
    +                xy_to_z_cross_term_admittance=arm_surface_contact_pb2.ArmSurfaceContact.Request.
    +                ADMITTANCE_SETTING_VERY_STIFF,
                     gripper_command=gripper_command)
     
                 # Enable walking
    diff --git a/python/examples/arm_surface_contact/requirements.txt b/python/examples/arm_surface_contact/requirements.txt
    index 5f0983252..ea085f145 100644
    --- a/python/examples/arm_surface_contact/requirements.txt
    +++ b/python/examples/arm_surface_contact/requirements.txt
    @@ -1,2 +1,2 @@
     -f ../../../prebuilt
    -bosdyn-client ~= 2.3
    +bosdyn-client >= 2.3
    diff --git a/python/examples/arm_trajectory/arm_trajectory.py b/python/examples/arm_trajectory/arm_trajectory.py
    index 11533e8ab..e5544e97f 100644
    --- a/python/examples/arm_trajectory/arm_trajectory.py
    +++ b/python/examples/arm_trajectory/arm_trajectory.py
    @@ -27,7 +27,6 @@
     from bosdyn.api import synchronized_command_pb2
     from bosdyn.api import robot_command_pb2
     
    -
     import traceback
     import time
     
    @@ -74,24 +73,24 @@ def arm_trajectory(config):
     
                 time.sleep(2.0)
     
    -
                 # Move the arm along a simple trajectory
     
    -            vo_T_flat_body = get_a_tform_b(robot_state_client.get_robot_state().kinematic_state.transforms_snapshot,
    -                          VISION_FRAME_NAME, GRAV_ALIGNED_BODY_FRAME_NAME)
    +            vo_T_flat_body = get_a_tform_b(
    +                robot_state_client.get_robot_state().kinematic_state.transforms_snapshot,
    +                VISION_FRAME_NAME, GRAV_ALIGNED_BODY_FRAME_NAME)
     
    -            x = 0.75     # a reasonable position in front of the robot
    -            y1 = 0 # centered
    -            y2 = 0.4 # 0.4 meters to the robot's left
    -            y3 = -0.4 # 0.4 meters to the robot's right
    -            z = 0       # at the body's height
    +            x = 0.75  # a reasonable position in front of the robot
    +            y1 = 0  # centered
    +            y2 = 0.4  # 0.4 meters to the robot's left
    +            y3 = -0.4  # 0.4 meters to the robot's right
    +            z = 0  # at the body's height
     
                 # Use the same rotation as the robot's body.
                 rotation = math_helpers.Quat()
     
    -            t_first_point = 0    # first point starts at t = 0 for the trajectory.
    -            t_second_point = 4.0 # trajectory will last 1.0 seconds
    -            t_third_point = 8.0 # trajectory will last 1.0 seconds
    +            t_first_point = 0  # first point starts at t = 0 for the trajectory.
    +            t_second_point = 4.0  # trajectory will last 1.0 seconds
    +            t_third_point = 8.0  # trajectory will last 1.0 seconds
     
                 # Build the two points in the trajectory
                 hand_pose1 = math_helpers.SE3Pose(x=x, y=y1, z=z, rot=rotation)
    @@ -102,7 +101,8 @@ def arm_trajectory(config):
                 traj_point1 = trajectory_pb2.SE3TrajectoryPoint(
                     pose=hand_pose1.to_proto(), time_since_reference=seconds_to_duration(t_first_point))
                 traj_point2 = trajectory_pb2.SE3TrajectoryPoint(
    -                pose=hand_pose2.to_proto(), time_since_reference=seconds_to_duration(t_second_point))
    +                pose=hand_pose2.to_proto(),
    +                time_since_reference=seconds_to_duration(t_second_point))
                 traj_point3 = trajectory_pb2.SE3TrajectoryPoint(
                     pose=hand_pose3.to_proto(), time_since_reference=seconds_to_duration(t_third_point))
     
    @@ -128,7 +128,8 @@ def arm_trajectory(config):
                     synchronized_command=synchronized_command)
     
                 # Keep the gripper closed the whole time.
    -            robot_command = RobotCommandBuilder.claw_gripper_open_fraction_command(0, build_on_command=robot_command)
    +            robot_command = RobotCommandBuilder.claw_gripper_open_fraction_command(
    +                0, build_on_command=robot_command)
     
                 robot.logger.info("Sending trajectory command...")
     
    @@ -138,7 +139,13 @@ def arm_trajectory(config):
                 # Wait until the arm arrives at the goal.
                 while True:
                     feedback_resp = command_client.robot_command_feedback(cmd_id)
    -                robot.logger.info('Distance to final point: ' + '{:.2f} meters'.format(feedback_resp.feedback.synchronized_feedback.arm_command_feedback.arm_cartesian_feedback.measured_pos_distance_to_goal) + ', {:.2f} radians'.format(feedback_resp.feedback.synchronized_feedback.arm_command_feedback.arm_cartesian_feedback.measured_rot_distance_to_goal))
    +                robot.logger.info(
    +                    'Distance to final point: ' + '{:.2f} meters'.format(
    +                        feedback_resp.feedback.synchronized_feedback.arm_command_feedback.
    +                        arm_cartesian_feedback.measured_pos_distance_to_goal) +
    +                    ', {:.2f} radians'.format(
    +                        feedback_resp.feedback.synchronized_feedback.arm_command_feedback.
    +                        arm_cartesian_feedback.measured_rot_distance_to_goal))
     
                     if feedback_resp.feedback.synchronized_feedback.arm_command_feedback.arm_cartesian_feedback.status == arm_command_pb2.ArmCartesianCommand.Feedback.STATUS_TRAJECTORY_COMPLETE:
                         robot.logger.info('Move complete.')
    diff --git a/python/examples/arm_trajectory/requirements.txt b/python/examples/arm_trajectory/requirements.txt
    index 5f0983252..ea085f145 100644
    --- a/python/examples/arm_trajectory/requirements.txt
    +++ b/python/examples/arm_trajectory/requirements.txt
    @@ -1,2 +1,2 @@
     -f ../../../prebuilt
    -bosdyn-client ~= 2.3
    +bosdyn-client >= 2.3
    diff --git a/python/examples/arm_walk_to_object/arm_walk_to_object.py b/python/examples/arm_walk_to_object/arm_walk_to_object.py
    index 141d3a1b8..d972a184e 100644
    --- a/python/examples/arm_walk_to_object/arm_walk_to_object.py
    +++ b/python/examples/arm_walk_to_object/arm_walk_to_object.py
    @@ -32,6 +32,7 @@
     g_image_click = None
     g_image_display = None
     
    +
     def walk_to_object(config):
         """Get an image and command the robot to walk up to a selected object.
            We'll walk "up to" the object, not on top of it.  The idea is that you
    @@ -114,12 +115,11 @@ def walk_to_object(config):
                         print('"q" pressed, exiting.')
                         exit(0)
     
    -            robot.logger.info('Walking to object at image location (' + str(g_image_click[0]) + ', ' +
    -                              str(g_image_click[1]) + ')')
    +            robot.logger.info('Walking to object at image location (' + str(g_image_click[0]) +
    +                              ', ' + str(g_image_click[1]) + ')')
     
                 walk_vec = geometry_pb2.Vec2(x=g_image_click[0], y=g_image_click[1])
     
    -
                 # Optionally populate the offset distance parameter.
                 if config.distance is None:
                     offset_distance = None
    @@ -150,10 +150,8 @@ def walk_to_object(config):
                     response = manipulation_api_client.manipulation_api_feedback_command(
                         manipulation_api_feedback_request=feedback_request)
     
    -                print(
    -                    'Current state: ',
    -                    manipulation_api_pb2.ManipulationFeedbackState.Name(
    -                        response.current_state))
    +                print('Current state: ',
    +                      manipulation_api_pb2.ManipulationFeedbackState.Name(response.current_state))
     
                     if response.current_state == manipulation_api_pb2.MANIP_STATE_DONE:
                         break
    @@ -190,6 +188,7 @@ def cv_mouse_callback(event, x, y, flags, param):
             cv2.line(clone, (x, 0), (x, height), color, thickness)
             cv2.imshow(image_title, clone)
     
    +
     def arg_float(x):
         try:
             x = float(x)
    @@ -197,13 +196,15 @@ def arg_float(x):
             raise argparse.ArgumentTypeError("%r not a number" % (x))
         return x
     
    +
     def main(argv):
         """Command line interface."""
         parser = argparse.ArgumentParser()
         bosdyn.client.util.add_common_arguments(parser)
         parser.add_argument('-i', '--image-source', help='Get image from source',
                             default='frontleft_fisheye_image')
    -    parser.add_argument('-d', '--distance', help='Distance from object to walk to (meters).', default=None, type=arg_float)
    +    parser.add_argument('-d', '--distance', help='Distance from object to walk to (meters).',
    +                        default=None, type=arg_float)
         options = parser.parse_args(argv)
     
         try:
    diff --git a/python/examples/arm_walk_to_object/requirements.txt b/python/examples/arm_walk_to_object/requirements.txt
    index 029684b2a..4f39d4137 100644
    --- a/python/examples/arm_walk_to_object/requirements.txt
    +++ b/python/examples/arm_walk_to_object/requirements.txt
    @@ -1,5 +1,5 @@
     -f ../../../prebuilt
    -bosdyn-client ~= 2.3
    +bosdyn-client >= 2.3
     
     opencv-python >= 3.4.2.17
     numpy >= 1.16.4
    diff --git a/python/examples/arm_with_body_follow/requirements.txt b/python/examples/arm_with_body_follow/requirements.txt
    index 5f0983252..ea085f145 100644
    --- a/python/examples/arm_with_body_follow/requirements.txt
    +++ b/python/examples/arm_with_body_follow/requirements.txt
    @@ -1,2 +1,2 @@
     -f ../../../prebuilt
    -bosdyn-client ~= 2.3
    +bosdyn-client >= 2.3
    diff --git a/python/examples/auto_return/README.md b/python/examples/auto_return/README.md
    new file mode 100644
    index 000000000..00bcc8e79
    --- /dev/null
    +++ b/python/examples/auto_return/README.md
    @@ -0,0 +1,24 @@
    +
    +
    +# Auto Return Example
    +
    +This example starts AutoReturn functionality manually with a force-acquired lease.
    +
    +## Setup Dependencies
    +These examples need to be run with python3, and have the Spot SDK installed. See the requirements.txt file for a list of dependencies which can be installed with pip.
    +```
    +$ python3 -m pip install -r requirements.txt
    +```
    +
    +## Run the Example
    +To run the example:
    +```
    +python3 force_start_auto_return.py --username USER --password PASSWORD ROBOT_IP
    +```
    +
    diff --git a/python/examples/auto_return/force_start_auto_return.py b/python/examples/auto_return/force_start_auto_return.py
    new file mode 100644
    index 000000000..23fb9ca5e
    --- /dev/null
    +++ b/python/examples/auto_return/force_start_auto_return.py
    @@ -0,0 +1,50 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""Start AutoReturn manually with a force-acquired lease."""
    +
    +import argparse
    +
    +import bosdyn.client
    +import bosdyn.client.util
    +from bosdyn.api.auto_return import auto_return_pb2
    +from bosdyn.client.auto_return import AutoReturnClient
    +from bosdyn.client.lease import LeaseClient
    +from bosdyn.util import seconds_to_duration
    +
    +
    +def main():
    +    """Run program."""
    +    parser = argparse.ArgumentParser()
    +    bosdyn.client.util.add_common_arguments(parser)
    +    parser.add_argument('--disp', type=float, help='Maximum displacement to travel (m)',
    +                        default=12.5)
    +    parser.add_argument('--duration', type=float, help='Maximum duration (s)')
    +    options = parser.parse_args()
    +    bosdyn.client.util.setup_logging(options.verbose)
    +
    +    sdk = bosdyn.client.create_standard_sdk('AutoReturnExample')
    +    robot = sdk.create_robot(options.hostname)
    +    robot.authenticate(options.username, options.password)
    +
    +    # Forcibly take the lease.
    +    lease_client = robot.ensure_client(LeaseClient.default_service_name)
    +    lease_client.take()
    +
    +    # Configure AutoReturn with the latest lease.
    +    autoreturn_client = robot.ensure_client(AutoReturnClient.default_service_name)
    +    params = auto_return_pb2.Params(max_displacement=options.disp)
    +    if options.duration:
    +        params.max_duration.CopyFrom(seconds_to_duration(options.duration))
    +
    +    autoreturn_client.configure(params, [lease_client.lease_wallet.get_lease().create_newer()])
    +
    +    # Begin the AutoReturn logic.
    +    autoreturn_client.start()
    +
    +
    +if __name__ == "__main__":
    +    main()
    diff --git a/python/examples/auto_return/requirements.txt b/python/examples/auto_return/requirements.txt
    new file mode 100644
    index 000000000..c3b7c9327
    --- /dev/null
    +++ b/python/examples/auto_return/requirements.txt
    @@ -0,0 +1,3 @@
    +-f ../../../prebuilt
    +
    +bosdyn-client >= 3.0
    \ No newline at end of file
    diff --git a/python/examples/bddf_download/README.md b/python/examples/bddf_download/README.md
    index 9e6b62839..370fd4241 100644
    --- a/python/examples/bddf_download/README.md
    +++ b/python/examples/bddf_download/README.md
    @@ -18,7 +18,7 @@ $ python3 -m pip install -r requirements.txt
     
     ## Running the Examples
     
    -There are two scripts in this example.  The first is `bddf_download.py`, which downloads specified data from a robot and saves it as a bddf file.
    +There are two primary scripts in this example.  The first is `bddf_download.py`, which downloads specified data from a robot and saves it as a bddf file.
     
     Another example script is `bddf_read.py`, which extracts and prints protobuf messages stored in a bddf file.
     
    @@ -117,3 +117,16 @@ $ ${DOWNLOAD_BDDF} --timespan 20m-10m --service robot-id -o robot-id.bddf
     $ python3 ./bddf_read.py show-grpc robot-id.bddf bosdyn.api.RobotIdRequest
     $ python3 ./bddf_read.py show-grpc robot-id.bddf bosdyn.api.RobotIdResponse
     ```
    +
    +## GUI
    +
    +A graphical user interface is also available using `bddf_download_gui.py`. An additional PyQt5 dependency is required.
    +
    +```
    +$ python3 -m pip install -r gui_requirements.txt
    +```
    +
    +To run the example as a GUI:
    +```
    +$ python3 bddf_download_gui.py
    +```
    \ No newline at end of file
    diff --git a/python/examples/bddf_download/bddf_download.py b/python/examples/bddf_download/bddf_download.py
    index 28ba3f96b..1d925731b 100644
    --- a/python/examples/bddf_download/bddf_download.py
    +++ b/python/examples/bddf_download/bddf_download.py
    @@ -19,7 +19,8 @@
     import bosdyn.client.util
     
     # Suppress only the single warning from urllib3 needed.
    -requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
    +requests.packages.urllib3.disable_warnings(  # pylint:disable=no-member
    +    category=InsecureRequestWarning)
     
     LOGGER = logging.getLogger()
     
    @@ -110,10 +111,10 @@ def _time_str(timeval):
         return {"from_sec": str(_time_str(start_time)), "to_sec": str(_time_str(end_time))}
     
     
    -def output_filename(options, response):
    +def output_filename(output, response):
         """Get output filename either from command line options or http response, or default value."""
    -    if options.output:
    -        return options.output
    +    if output:
    +        return output
         content = response.headers['Content-Disposition']
         if len(content) < 2:
             LOGGER.debug("Content-Disposition not set correctly.")
    @@ -124,36 +125,17 @@ def output_filename(options, response):
         return match.group(1)
     
     
    -def main():  # pylint: disable=too-many-locals
    -    """Command-line interface"""
    -    import argparse  # pylint: disable=import-outside-toplevel
    -    parser = argparse.ArgumentParser()
    -    parser.add_argument('-T', '--timespan', help='Time span (default last 5 minutes)')
    -    parser.add_argument('--help-timespan', action='store_true',
    -                        help='Print time span formatting options')
    -    parser.add_argument('-c', '--channel', help='Specify channel for data (default=all)')
    -    parser.add_argument('-t', '--type', help='Specify message type (default=all)')
    -    parser.add_argument('-s', '--service', help='Specify service name (default=all)')
    -    parser.add_argument('-o', '--output', help='Output file name (default is "download.bddf"')
    -
    -    bosdyn.client.util.add_common_arguments(parser)
    -    options = parser.parse_args()
    -
    -    if options.verbose:
    -        LOGGER.setLevel(logging.DEBUG)
    -
    -    if options.help_timespan:
    -        _print_help_timespan()
    -        return 0
    +def prepare_download(hostname, username, password, timespan, channel, message_type, service):
    +    """Prepares all arguments for http get request."""
     
         # Create a robot object.
         sdk = create_standard_sdk('bddf')
    -    robot = sdk.create_robot(options.hostname)
    +    robot = sdk.create_robot(hostname)
     
         # Use the robot object to authenticate to the robot.
         # A JWT Token is required to download log data.
         try:
    -        robot.authenticate(options.username, options.password)
    +        robot.authenticate(username, password)
         except RpcError as err:
             LOGGER.error("Cannot authenticate to robot to obtain token: %s", err)
             return 1
    @@ -167,33 +149,91 @@ def main():  # pylint: disable=too-many-locals
                          time_sync_endpoint.clock_skew.seconds, time_sync_endpoint.clock_skew.nanos)
     
         # Now assemble the query to obtain a bddf file.
    -    url = 'https://{}/v1/data-buffer/bddf/'.format(options.hostname)
    +    url = 'https://{}/v1/data-buffer/bddf/'.format(hostname)
         headers = {"Authorization": "Bearer {}".format(robot.user_token)}
     
         # Get the parameters for limiting the timespan of the response.
    -    get_params = request_timespan(options.timespan, time_sync_endpoint)
    +    get_params = request_timespan(timespan, time_sync_endpoint)
     
         # Optional parameters for limiting the messages
    -    if options.channel:
    -        get_params['channel'] = options.channel
    -    if options.type:
    -        get_params['type'] = options.type
    -    if options.service:
    -        get_params['grpc_service'] = options.service
    -
    -    # Request the data.
    -    with requests.get(url, headers=headers, verify=False, stream=True, params=get_params) as resp:
    +    if channel:
    +        get_params['channel'] = channel
    +    if message_type:
    +        get_params['type'] = message_type
    +    if service:
    +        get_params['grpc_service'] = service
    +
    +    return url, headers, get_params
    +
    +
    +def collect_and_write_file(url, headers, parameters, output):
    +    """Downloads data and writes it to a file."""
    +    outfile = None
    +    with requests.get(url, headers=headers, verify=False, stream=True, params=parameters) as resp:
    +        if 'content-length' in resp.headers:
    +            total_content_length = resp.headers['content-length']
    +        else:
    +            # Transfer encoding is chunked.
    +            total_content_length = 0
             if resp.status_code != 200:
    -            LOGGER.error("https response: %d", resp.status_code)
    -            sys.exit(1)
    -        outfile = output_filename(options, resp)
    -        with open(outfile, 'wb') as fid:
    -            for chunk in resp.iter_content(chunk_size=REQUEST_CHUNK_SIZE):
    -                print('.', end='', flush=True)
    -                fid.write(chunk)
    -        print()
    +            LOGGER.error("Unable to get data. https response: %d", resp.status_code)
    +            yield None, None, resp.status_code
    +        else:
    +            outfile = output_filename(output, resp)
    +            with open(outfile, 'wb') as fid:
    +                for chunk in resp.iter_content(chunk_size=REQUEST_CHUNK_SIZE):
    +                    fid.write(chunk)
    +                    yield len(chunk), int(total_content_length), resp.status_code
     
         LOGGER.info("Wrote '%s'.", outfile)
    +
    +
    +def main():  # pylint: disable=too-many-locals
    +    """Command-line interface"""
    +    import argparse  # pylint: disable=import-outside-toplevel
    +    parser = argparse.ArgumentParser()
    +    parser.add_argument('-T', '--timespan', help='Time span (default last 5 minutes)')
    +    parser.add_argument('--help-timespan', action='store_true',
    +                        help='Print time span formatting options')
    +    parser.add_argument('-c', '--channel', nargs='+',
    +                        help='Specify channel(s) for data (default=all)')
    +    parser.add_argument('-t', '--type', help='Specify message type (default=all)')
    +    parser.add_argument('-s', '--service', help='Specify service name (default=all)')
    +    parser.add_argument('-o', '--output', help='Output file name (default is "download.bddf"')
    +
    +    bosdyn.client.util.add_common_arguments(parser)
    +    options = parser.parse_args()
    +
    +    if options.verbose:
    +        LOGGER.setLevel(logging.DEBUG)
    +
    +    if options.help_timespan:
    +        _print_help_timespan()
    +        return 0
    +
    +    url, headers, params = prepare_download(options.hostname, options.username, options.password, \
    +                                                options.timespan, \
    +                                                options.channel, options.type, options.service)
    +
    +    number_of_bytes_processed = 0
    +    for chunk, total_content_length, response_status_code in collect_and_write_file(
    +            url, headers, params, options.output):
    +        # Check for success status response.
    +        if response_status_code != 200:
    +            return False
    +        else:
    +            # Calculate and write status updates.
    +            number_of_bytes_processed = number_of_bytes_processed + chunk
    +            total_size_of_request = total_content_length
    +            if total_size_of_request == 0:
    +                print(
    +                    f"Data is chunked. Number of megabytes processed: {number_of_bytes_processed/1e6:.0f} [MB].",
    +                    end="\r")
    +            else:
    +                percentage_compete = (number_of_bytes_processed / total_size_of_request) * 100
    +                print(f"Download is {percentage_compete:.2f}% complete.", end="\r")
    +    print()
    +
         return 0
     
     
    diff --git a/python/examples/bddf_download/bddf_download_gui.py b/python/examples/bddf_download/bddf_download_gui.py
    new file mode 100644
    index 000000000..ae4f1cb6d
    --- /dev/null
    +++ b/python/examples/bddf_download/bddf_download_gui.py
    @@ -0,0 +1,388 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""pyqt wrapper for bddf_download.py"""
    +
    +import sys
    +import os
    +import argparse
    +import datetime
    +import platform
    +import logging
    +import signal
    +import subprocess
    +
    +from PyQt5 import QtCore, QtWidgets
    +from PyQt5 import uic
    +
    +from bddf_download import prepare_download, collect_and_write_file
    +import bosdyn.client
    +from bosdyn.util import duration_str
    +from bosdyn.client.time_sync import TimeSyncClient, TimeSyncEndpoint
    +from bosdyn.client.data_service import DataServiceClient
    +from bosdyn.api.data_index_pb2 import EventsCommentsSpec
    +
    +# Use for PyInstaller Executable.
    +QTCREATORFILE = 'download.ui'
    +if hasattr(sys, '_MEIPASS'):
    +    UI_PATH = os.path.join(sys._MEIPASS, QTCREATORFILE)
    +else:
    +    UI_PATH = QTCREATORFILE
    +
    +logging.basicConfig(level=logging.INFO)
    +
    +DEFAULT_STATUS_BAR_TIMEOUT = 3000  # ms
    +DATETIME_FORMAT = 'yyyyMMdd_hhmmss'
    +ORGANIZATION = "Boston Dynamics"
    +APPLICATION_NAME = "BDDF Download"
    +
    +
    +def simple_ping(hostname):
    +    """Sends a short ping request to the desired IPv4 hostname.
    +
    +    Args:
    +        hostname (string) IPv4 address of the robot
    +
    +    Returns:
    +        exit_code (int) return status from the ping request
    +    """
    +    current_os = platform.system().lower()
    +    if current_os == "windows":
    +        parameter = "-n"
    +    else:
    +        parameter = "-c"
    +    exit_code = subprocess.call(['ping', parameter, '1', '-w2', hostname],
    +                                stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
    +    return exit_code
    +
    +
    +class BDDFDownloadGui(QtWidgets.QMainWindow):
    +    """Graphical user interface to download BDDF logs from the robot."""
    +
    +    def __init__(self, *args, **kwargs):
    +        super(BDDFDownloadGui, self).__init__(*args, **kwargs)
    +        uic.loadUi(UI_PATH, self)
    +
    +        self.setup_default_time(minutes_before=30)
    +        self.sdk = bosdyn.client.create_standard_sdk('bddf_download_gui.py')
    +
    +        # Button clicking.
    +        self.btn_download.clicked.connect(self.download)
    +        self.btn_ping.clicked.connect(self.ping)
    +        self.btn_select_all.clicked.connect(self.select_all)
    +        self.btn_save_as.clicked.connect(self.save_as)
    +        self.btn_check_timesync.clicked.connect(self.check_timesync)
    +        self.btn_check_data_buffer.clicked.connect(self.check_data_buffer_size)
    +        self.pb_get_events_comments.clicked.connect(self.display_operator_comments)
    +
    +
    +        # If any input field is changed, regenerate the command.
    +        self.le_hostname.textChanged.connect(self.generate_download_command)
    +        self.le_hostname.textChanged.connect(self.clear_required_fields)
    +        self.le_username.textChanged.connect(self.generate_download_command)
    +        self.le_password.textChanged.connect(self.generate_download_command)
    +        self.le_output_filepath.textChanged.connect(self.generate_download_command)
    +        self.le_output_filename.textChanged.connect(self.generate_download_command)
    +        self.le_val.textChanged.connect(self.generate_download_command)
    +        self.le_channel.textChanged.connect(self.generate_download_command)
    +        self.dte_1.dateTimeChanged.connect(self.generate_download_command)
    +        self.dte_2.dateTimeChanged.connect(self.generate_download_command)
    +        self.cb_use_date_time.stateChanged.connect(self.generate_download_command)
    +        self.cb_append_timespan.stateChanged.connect(self.generate_download_command)
    +        self.cb_use_date_time.stateChanged.connect(self.use_date_time_switch)
    +
    +        # Use QSettings for application state.
    +        self.settings = QtCore.QSettings(ORGANIZATION, APPLICATION_NAME)
    +        self.settings_dict = {
    +            'hostname': self.le_hostname,
    +            'username': self.le_username,
    +            'password': self.le_password,
    +            'output_filepath': self.le_output_filepath,
    +            'channel': self.le_channel,
    +        }
    +        self.readSettings()
    +
    +        self.generate_download_command()
    +
    +    def line_edit_settings(self, write=False):
    +        """Function helper for reading and writing to QLineEdits of previous user session.
    +
    +        Args:
    +            write (bool) read or write to line edit.
    +        """
    +        for key, value in self.settings_dict.items():
    +            if write:
    +                self.settings.setValue(key, value.text())
    +            else:
    +                value.setText(self.settings.value(key))
    +
    +    def readSettings(self):
    +        """Read from QSettings."""
    +        self.line_edit_settings(write=False)
    +
    +    def writeSettings(self):
    +        """Write to QSettings."""
    +        self.line_edit_settings(write=True)
    +
    +    def closeEvent(self, event):
    +        """Call writeSettings on window close event."""
    +        self.writeSettings()
    +
    +    def clear_required_fields(self):
    +        """Clear the timesync and data buffer fields if hostname changes."""
    +        self.le_check_data_buffer.clear()
    +        self.le_timesync.clear()
    +
    +    def get_authenticated_robot_client(self):
    +        """Returns authenticated robot sdk object."""
    +        robot = self.sdk.create_robot(self.le_hostname.text())
    +        # Check if communication is possible.
    +        if not self.ping():
    +            return False
    +        try:
    +            robot.authenticate(self.le_username.text(), self.le_password.text())
    +            return robot
    +        except Exception as e:
    +            error_message = f'Failed to connect/authenticate: {e}'
    +            self.statusBar().showMessage(error_message, DEFAULT_STATUS_BAR_TIMEOUT)
    +            return False
    +
    +    def check_data_buffer_size(self):
    +        """Displays the current robot data buffer size."""
    +        robot = self.get_authenticated_robot_client()
    +        if robot is not False:
    +            client = robot.ensure_client(DataServiceClient.default_service_name)
    +            status = client.get_data_buffer_status()
    +            data_buffer_size = status.data_buffer_status.data_buffer_total_bytes
    +            str_data_buffer_size = f'{data_buffer_size/1e9:0.3f} GB'
    +            self.le_check_data_buffer.setText(str_data_buffer_size)
    +
    +    def check_timesync(self):
    +        """Check and report time difference between robot and client."""
    +        clock_skew = self.get_clock_skew()
    +        if clock_skew:
    +            self.le_timesync.setText(duration_str(clock_skew))
    +
    +    def get_clock_skew(self):
    +        """Returns the client to robot clock_skew."""
    +        robot = self.get_authenticated_robot_client()
    +        if robot is not False:
    +            endpoint = TimeSyncEndpoint(robot.ensure_client(TimeSyncClient.default_service_name))
    +            if not endpoint.establish_timesync(break_on_success=False):
    +                logging.debug("Failed to achieve time sync.")
    +                return False
    +            return endpoint.clock_skew
    +
    +    def generate_download_command(self):
    +        """Generate equivalent bddf_download.py command."""
    +        hostname, username, password, timespan, output, channel = self.return_all_arguments()
    +
    +        command = (f'python3 bddf_download.py {hostname} '
    +                   f'--username {username} '
    +                   f'--password PASSWORD '
    +                   f'-T {timespan} '
    +                   f'-o {output}')
    +
    +        # If channel is defined, append as an argument.
    +        if channel:
    +            command = f'{command} -c {channel}'
    +
    +        self.te_command.setText(command)
    +
    +    def return_all_arguments(self):
    +        """Return all arguments required for simple bddf_download.py."""
    +        hostname = self.le_hostname.text()
    +        username = self.le_username.text()
    +        password = self.le_password.text()
    +        timespan = self.get_timespan_string()
    +        output = self.get_output()
    +        channel = self.le_channel.text()
    +
    +        return hostname, username, password, timespan, output, channel
    +
    +    def save_as(self):
    +        """Use file browser to specify output filepath and filename."""
    +        output = self.get_output()
    +        file_name, _ = QtWidgets.QFileDialog.getSaveFileName(self, "Save As", output,
    +                                                             "BDDF Files (*.bddf)")
    +        output_filepath, output_filename = os.path.split(file_name)
    +        self.le_output_filepath.setText(output_filepath)
    +        self.le_output_filename.setText(output_filename)
    +
    +    def select_all(self):
    +        """Quickly select all text used for bddf_download.py command."""
    +        self.te_command.selectAll()
    +
    +    def get_output(self):
    +        """Get output filepath and filename for format for bddf_download.py."""
    +        output_filepath = self.le_output_filepath.text()
    +        output_filename = self.le_output_filename.text()
    +
    +        file_extension = '.bddf'
    +        output_filename = os.path.splitext(output_filename)[0]
    +        if self.cb_append_timespan.isChecked():
    +            output_filename = f'{output_filename}_{self.get_timespan_string()}{file_extension}'
    +        else:
    +            output_filename = f'{output_filename}{file_extension}'
    +
    +        output_filename = output_filename.replace(" ", "_")
    +        total_filepath = os.path.join(output_filepath, output_filename)
    +
    +        return total_filepath
    +
    +    def setup_default_time(self, minutes_before):
    +        """Default time options on program startup.
    +
    +        Args:
    +            minutes_before (int) number of minutes to use for the datetime timespan.
    +        """
    +        current_time = QtCore.QDateTime.currentDateTime()
    +        # Set second {val} as current time.
    +        self.dte_2.setDateTime(current_time)
    +        python_current_time = current_time.toPyDateTime()
    +        # Set first {val} sometime before.
    +        val_1_time = python_current_time - datetime.timedelta(minutes=minutes_before)
    +        self.dte_1.setDateTime(val_1_time)
    +
    +    def get_timespan_string(self):
    +        """Get timespan string from date time edits and format for bddf_download.py."""
    +        raise_filesize_limit_flag = False
    +        timespan = None
    +
    +        if self.cb_use_date_time.isChecked():
    +            datetime_1 = self.dte_1.dateTime()
    +            datetime_2 = self.dte_2.dateTime()
    +
    +            if datetime_2.toPyDateTime() - datetime_1.toPyDateTime() > datetime.timedelta(hours=1):
    +                raise_filesize_limit_flag = True
    +            else:
    +                raise_filesize_limit_flag = False
    +
    +            timespan = datetime_1.toString(DATETIME_FORMAT) + '-' + datetime_2.toString(
    +                DATETIME_FORMAT)
    +        else:
    +            timespan = self.le_val.text()
    +
    +        if raise_filesize_limit_flag is True:
    +            self.statusBar().showMessage(f'File size to large. Do not exceed 60 minutes.',
    +                                         DEFAULT_STATUS_BAR_TIMEOUT)
    +            self.statusBar().setStyleSheet("background-color : red")
    +        else:
    +            self.statusBar().setStyleSheet("")
    +
    +        return timespan
    +
    +    def download(self):
    +        """Perform the BDDF download and update with download percentage."""
    +        hostname, username, password, timespan, output, channel = self.return_all_arguments()
    +        if not hostname:
    +            self.statusBar().showMessage(f'Please specify a hostname.', DEFAULT_STATUS_BAR_TIMEOUT)
    +            return
    +
    +        robot = self.get_authenticated_robot_client()
    +        if robot is False:
    +            return
    +
    +        output = os.path.expanduser(output)
    +        if os.path.exists(output):
    +            self.statusBar().showMessage(f'Filepath already exists: {output}',
    +                                         DEFAULT_STATUS_BAR_TIMEOUT)
    +            return
    +
    +        filepath = os.path.split(output)[0]
    +        if not os.path.exists(filepath):
    +            os.makedirs(filepath, exist_ok=True)
    +
    +        self.statusBar().showMessage('Download started, please wait...', DEFAULT_STATUS_BAR_TIMEOUT)
    +
    +        # If channel is defined, use as an argument.
    +        use_channel = None
    +        if channel:
    +            use_channel = channel.split()  # for multiple channels, split by space character
    +
    +        url, headers, params = prepare_download(hostname, username, password, timespan, \
    +                                                channel=use_channel, message_type=None, service=None)
    +
    +        self.pb.setValue(0)
    +        number_of_bytes_processed = 0
    +        for chunk, total_content_length, response_status_code in collect_and_write_file(
    +                url, headers, params, output):
    +            # Check for success status response.
    +            if response_status_code != 200:
    +                self.statusBar().showMessage(
    +                    f'Unable to get data. https response: {response_status_code}',
    +                    DEFAULT_STATUS_BAR_TIMEOUT)
    +                return False
    +            else:
    +                number_of_bytes_processed = number_of_bytes_processed + chunk
    +                total_size_of_request = total_content_length
    +                if total_size_of_request == 0:
    +                    bytes_processed_string = f'Data is chunked. Number of megabytes processed: {number_of_bytes_processed/1e6:.0f} [MB].'
    +                    self.label_filesize.setText(bytes_processed_string)
    +                else:
    +                    self.label_filesize.setText(
    +                        f'Download progress: {number_of_bytes_processed/1e6:.2f} [MB] of {total_size_of_request/1e6:.2f} [MB]'
    +                    )
    +                    percentage_compete = (number_of_bytes_processed / total_size_of_request) * 100
    +                    download_percentage_string = f"Download is {percentage_compete:.2f}% complete."
    +                    logging.debug(download_percentage_string)
    +                    self.pb.setValue(percentage_compete)
    +                # Hack to get the label_filesize to update quickly.
    +                QtCore.QCoreApplication.processEvents()
    +
    +        self.statusBar().showMessage(f'Download complete: {output}')
    +
    +    def ping(self):
    +        """Ping robot and update status bar."""
    +        hostname = self.le_hostname.text()
    +        status = simple_ping(hostname)
    +        if status == 0:
    +            self.statusBar().showMessage(f'Ping of {hostname} was successful.',
    +                                         DEFAULT_STATUS_BAR_TIMEOUT)
    +            return True
    +        else:
    +            self.statusBar().showMessage(f'Ping of {hostname} FAILED.', DEFAULT_STATUS_BAR_TIMEOUT)
    +            return False
    +
    +    def use_date_time_switch(self, new_state):
    +        """Toggle timespan options.
    +
    +        Args:
    +            new_state (int) New checked state return from QCheckBox.
    +        """
    +        if new_state == 2:
    +            self.dte_1.setEnabled(True)
    +            self.dte_2.setEnabled(True)
    +            self.le_val.setEnabled(False)
    +        else:
    +            self.dte_1.setEnabled(False)
    +            self.dte_2.setEnabled(False)
    +            self.le_val.setEnabled(True)
    +
    +    def display_operator_comments(self):
    +        """Display operator comments in QListWidget."""
    +        robot = self.get_authenticated_robot_client()
    +        if robot is not False:
    +            data_service_client = robot.ensure_client(DataServiceClient.default_service_name)
    +            data_response = data_service_client.get_events_comments(
    +                EventsCommentsSpec(comments=True))
    +            self.lw_comments.clear()
    +            for comment in data_response.events_comments.operator_comments:
    +                string_to_display = f'{comment.timestamp.ToDatetime().strftime("%Y-%m-%d %H:%M:%S")} | {comment.message}'
    +                self.lw_comments.addItem(string_to_display)
    +
    +
    +def main():
    +    """Start GUI and handle interrupt."""
    +    signal.signal(signal.SIGINT, signal.SIG_DFL)
    +    app = QtWidgets.QApplication(sys.argv)
    +    window = BDDFDownloadGui()
    +    window.show()
    +    sys.exit(app.exec_())
    +
    +
    +if __name__ == '__main__':
    +    main()
    diff --git a/python/examples/bddf_download/bddf_read.py b/python/examples/bddf_download/bddf_read.py
    index 52bd92690..8a985ecf1 100644
    --- a/python/examples/bddf_download/bddf_read.py
    +++ b/python/examples/bddf_download/bddf_read.py
    @@ -15,13 +15,14 @@
     LOGGER = logging.getLogger()
     
     
    -def get_data(proto_type, filename):
    +def get_data(proto_type, filename, channel=None):
         """Iterator for message of the given protobuf type stored in bddf file 'filename'."""
         with open(filename, 'rb') as infile:
             data_reader = DataReader(infile)
             protobuf_reader = ProtobufReader(data_reader)
             try:
    -            channel_reader = ProtobufChannelReader(protobuf_reader, proto_type)
    +            channel_reader = ProtobufChannelReader(protobuf_reader, proto_type,
    +                                                   channel_name=channel)
             except KeyError:
                 LOGGER.error("No messages of type '%s' found", proto_type.DESCRIPTOR.full_name)
                 sys.exit(1)
    @@ -70,8 +71,8 @@ def __init__(self):
     
         def show(self, timestamp_msg):
             msg = timestamp_msg[1]
    -        timestamp = datetime.datetime.fromtimestamp(
    -            msg.timestamp.seconds + 1e-9 * msg.timestamp.nanos)
    +        timestamp = datetime.datetime.fromtimestamp(msg.timestamp.seconds +
    +                                                    1e-9 * msg.timestamp.nanos)
             print('{}   {}'.format(timestamp, msg.message.strip()))
     
     
    @@ -141,9 +142,11 @@ def setup():
         # pylint: disable=import-outside-toplevel
         import bosdyn.api.data_buffer_pb2
         import bosdyn.api.robot_id_pb2
    +    import bosdyn.api.robot_state_pb2
         index_protos(bosdyn.api.data_buffer_pb2)
         index_protos(bosdyn.api.robot_id_pb2)
    -    # Specialized listeners
    +    index_protos(bosdyn.api.robot_state_pb2)
    +    # Specialized listers
         EventLister()
         OperatorCommentLister()
     
    @@ -160,7 +163,7 @@ def list_series(options):
             series_identifiers = data_reader.file_index.series_identifiers
             print("Series in '{}' ({}):".format(options.filename, len(series_identifiers)))
             for i, sid in enumerate(series_identifiers):
    -            print("- [{}] {}: {}".format(i, sid.series_type, sid.spec))
    +            print("- [{}] {}: {}".format(i, sid.series_type, {k: v for k, v in sid.spec.items()}))
     
     
     def show_messages(options):
    @@ -171,7 +174,7 @@ def show_messages(options):
             print("No support for printing messages of type '{}'".format(options.message_type))
             sys.exit(1)
     
    -    for msg in get_data(lister.proto_type, options.filename):
    +    for msg in get_data(lister.proto_type, options.filename, channel=options.channel):
             lister.show(msg)
     
     
    @@ -205,6 +208,7 @@ def main():  # pylint: disable=too-many-locals
         show_parser = subparsers.add_parser('show', help='Show messages from a given channel')
         show_parser.add_argument('filename', default='download.bddf', help='input file')
         show_parser.add_argument('message_type', help='type of messages to show')
    +    show_parser.add_argument('--channel', help='name of channel, if not the same as message_type')
     
         grpc_parser = subparsers.add_parser('show-grpc', help='Show grpc requests/responses by type')
         grpc_parser.add_argument('filename', default='download.bddf', help='input file')
    diff --git a/python/examples/bddf_download/download.ui b/python/examples/bddf_download/download.ui
    new file mode 100644
    index 000000000..5797013bb
    --- /dev/null
    +++ b/python/examples/bddf_download/download.ui
    @@ -0,0 +1,577 @@
    +
    +
    + MainWindow
    + 
    +  
    +   true
    +  
    +  
    +   
    +    0
    +    0
    +    1280
    +    500
    +   
    +  
    +  
    +   BDDF Download
    +  
    +  
    +   
    +    
    +     10
    +    
    +    
    +     10
    +    
    +    
    +     10
    +    
    +    
    +     10
    +    
    +    
    +     
    +      
    +       
    +        
    +         Bosdyn API Client Common Arguments
    +        
    +       
    +      
    +      
    +       
    +        
    +         
    +          
    +           Hostname
    +          
    +         
    +        
    +        
    +         
    +          
    +           <html><head/><body><p>robot ipv4 address</p></body></html>
    +          
    +          
    +           192.168.80.3
    +          
    +         
    +        
    +        
    +         
    +          
    +           true
    +          
    +          
    +           ping
    +          
    +         
    +        
    +       
    +      
    +      
    +       
    +        
    +         
    +          
    +           Username
    +          
    +         
    +        
    +        
    +         
    +        
    +       
    +      
    +      
    +       
    +        
    +         
    +          
    +           Password 
    +          
    +         
    +        
    +        
    +         
    +          
    +           QLineEdit::PasswordEchoOnEdit
    +          
    +         
    +        
    +       
    +      
    +      
    +       
    +        
    +         
    +          
    +           true
    +          
    +          
    +           Check timesync
    +          
    +         
    +        
    +        
    +         
    +          
    +           true
    +          
    +          
    +           
    +          
    +         
    +        
    +       
    +      
    +      
    +       
    +        
    +         
    +          
    +           true
    +          
    +          
    +           Check data buffer size
    +          
    +         
    +        
    +        
    +         
    +          
    +           true
    +          
    +          
    +           
    +          
    +         
    +        
    +       
    +      
    +      
    +       
    +        
    +         Qt::Vertical
    +        
    +        
    +         
    +          20
    +          40
    +         
    +        
    +       
    +      
    +     
    +    
    +    
    +     
    +      
    +       
    +        
    +         <html><head/><body><p>A timespan is {val} or {val}-{val} where:</p><p><br/></p><p>{val} has one of these formats:</p><p> - yyyymmdd_hhmmss  (e.g., 20200120_120000)</p><p> - yyyymmdd         (e.g., 20200120)</p><p> -  {n}d    {n} days ago</p><p> -  {n}h    {n} hours ago</p><p> -  {n}m    {n} minutes ago</p><p> -  {n}s    {n} seconds ago</p><p> - nnnnnnnnnn[.nn]       (e.g., 1581869515.256)  Seconds since epoch</p><p> - nnnnnnnnnnnnnnnnnnnn  Nanoseconds since epoch</p><p><br/></p><p>For example:</p><p>  '5m'                    From 5 minutes ago until now.</p><p>  '20201107-20201108'     All of 2020/11/07.</p></body></html>
    +        
    +        
    +         Timespan (must be <60 minutes)
    +        
    +       
    +      
    +      
    +       
    +        
    +         true
    +        
    +        
    +         <html><head/><body><p>A timespan is {val} or {val}-{val} where:</p><p><br/></p><p>{val} has one of these formats:</p><p> - yyyymmdd_hhmmss  (e.g., 20200120_120000)</p><p> - yyyymmdd         (e.g., 20200120)</p><p> -  {n}d    {n} days ago</p><p> -  {n}h    {n} hours ago</p><p> -  {n}m    {n} minutes ago</p><p> -  {n}s    {n} seconds ago</p><p> - nnnnnnnnnn[.nn]       (e.g., 1581869515.256)  Seconds since epoch</p><p> - nnnnnnnnnnnnnnnnnnnn  Nanoseconds since epoch</p><p><br/></p><p>For example:</p><p>  '5m'                    From 5 minutes ago until now.</p><p>  '20201107-20201108'     All of 2020/11/07.</p></body></html>
    +        
    +        
    +         5m
    +        
    +       
    +      
    +      
    +       
    +        
    +         true
    +        
    +        
    +         
    +        
    +        
    +         
    +        
    +        
    +         Use Date/Time Selector
    +        
    +        
    +         false
    +        
    +       
    +      
    +      
    +       
    +        
    +         
    +          
    +           false
    +          
    +          
    +           
    +            0
    +            0
    +            0
    +            2021
    +            1
    +            1
    +           
    +          
    +          
    +           yyyy/MM/dd hh:mm:ss
    +          
    +          
    +           true
    +          
    +         
    +        
    +        
    +         
    +          
    +           Qt::Horizontal
    +          
    +          
    +           
    +            40
    +            20
    +           
    +          
    +         
    +        
    +        
    +         
    +          
    +           ---->
    +          
    +         
    +        
    +        
    +         
    +          
    +           Qt::Horizontal
    +          
    +          
    +           
    +            40
    +            20
    +           
    +          
    +         
    +        
    +        
    +         
    +          
    +           false
    +          
    +          
    +           
    +            23
    +            59
    +            0
    +            2021
    +            1
    +            1
    +           
    +          
    +          
    +           
    +          
    +          
    +           yyyy/MM/dd hh:mm:ss
    +          
    +          
    +           true
    +          
    +         
    +        
    +       
    +      
    +      
    +       
    +        
    +         true
    +        
    +        
    +         Append timespan to filename
    +        
    +       
    +      
    +      
    +       
    +        
    +         
    +          
    +           Channel(s):
    +          
    +         
    +        
    +        
    +         
    +          
    +           true
    +          
    +          
    +           <html><head/><body><p>Separate each channel by a space.</p></body></html>
    +          
    +          
    +           
    +          
    +          
    +           channel.1 channel.2
    +          
    +          
    +           false
    +          
    +         
    +        
    +       
    +      
    +      
    +       
    +        
    +         Qt::Vertical
    +        
    +        
    +         
    +          20
    +          40
    +         
    +        
    +       
    +      
    +     
    +    
    +    
    +     
    +      
    +       
    +        
    +         Operator Comments
    +        
    +       
    +      
    +      
    +       
    +        
    +         Get all comments
    +        
    +       
    +      
    +      
    +       
    +        
    +         
    +          500
    +          0
    +         
    +        
    +        
    +         true
    +        
    +        
    +         false
    +        
    +       
    +      
    +     
    +    
    +    
    +     
    +      
    +       
    +        
    +         
    +          
    +           bddf_download.py arguments
    +          
    +         
    +        
    +        
    +         
    +          
    +           Select All
    +          
    +         
    +        
    +       
    +      
    +      
    +       
    +        
    +         <html><head/><body><p><span style=" text-decoration: underline;">Example:</span></p><p>python3 bddf_download.py HOSTNAME -T 20210115_131800-20210115_140100 --username USER --password PASSWORD -o ~/Documents/download.bddf</p></body></html>
    +        
    +        
    +         <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +<html><head><meta name="qrichtext" content="1" /><style type="text/css">
    +p, li { white-space: pre-wrap; }
    +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;">
    +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Waiting for input...</p></body></html>
    +        
    +       
    +      
    +     
    +    
    +    
    +     
    +      
    +       
    +        
    +         
    +          
    +           Output filepath  
    +          
    +          
    +           Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
    +          
    +         
    +        
    +        
    +         
    +          
    +           
    +          
    +          
    +           ~/Documents/bddf_download_gui
    +          
    +         
    +        
    +        
    +         
    +          
    +           true
    +          
    +          
    +           <html><head/><body><p>Use the text inputs to the left or &quot;Save As&quot; to specify output filepath and filename before download.</p></body></html>
    +          
    +          
    +           Save As
    +          
    +         
    +        
    +       
    +      
    +      
    +       
    +        
    +         
    +          
    +           Output filename
    +          
    +         
    +        
    +        
    +         
    +          
    +           download.bddf
    +          
    +         
    +        
    +        
    +         
    +          
    +           <html><head/><body><p>Start download.</p></body></html>
    +          
    +          
    +           background-color: rgb(115, 210, 22);
    +          
    +          
    +           Download
    +          
    +         
    +        
    +       
    +      
    +      
    +       
    +        
    +         
    +          
    +           Download progress:
    +          
    +         
    +        
    +       
    +      
    +      
    +       
    +        
    +         0
    +        
    +       
    +      
    +     
    +    
    +   
    +  
    +  
    +  
    +   
    +    
    +     0
    +     0
    +     1280
    +     22
    +    
    +   
    +  
    +  
    +   
    +    debug
    +   
    +   
    +    Ctrl+D
    +   
    +   
    +    Qt::WindowShortcut
    +   
    +  
    +  
    +   
    +    DEFAULT
    +   
    +  
    +  
    +   
    +    Add New Profile
    +   
    +  
    +  
    +   
    +    info
    +   
    +   
    +    Ctrl+I
    +   
    +  
    +  
    +   
    +    Edit config.ini
    +   
    +  
    +  
    +   
    +    Reload config.ini
    +   
    +  
    +  
    +   
    +    DEFAULT
    +   
    +  
    + 
    + 
    + 
    +
    diff --git a/python/examples/bddf_download/gui_requirements.txt b/python/examples/bddf_download/gui_requirements.txt
    new file mode 100644
    index 000000000..dc9d417f9
    --- /dev/null
    +++ b/python/examples/bddf_download/gui_requirements.txt
    @@ -0,0 +1,6 @@
    +-f ../../../prebuilt
    +
    +bosdyn-client >= 2.1
    +
    +requests==2.24.0
    +PyQt5==5.15.2
    \ No newline at end of file
    diff --git a/python/examples/bddf_download/requirements.txt b/python/examples/bddf_download/requirements.txt
    index cc4f40fca..c4eb13e61 100644
    --- a/python/examples/bddf_download/requirements.txt
    +++ b/python/examples/bddf_download/requirements.txt
    @@ -1,5 +1,5 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.1
    +bosdyn-client >= 2.1
     
    -requests==2.24.0
    +requests==2.24.0
    \ No newline at end of file
    diff --git a/python/examples/cloud_upload/cloud_upload.py b/python/examples/cloud_upload/cloud_upload.py
    index df122c429..79c739a38 100644
    --- a/python/examples/cloud_upload/cloud_upload.py
    +++ b/python/examples/cloud_upload/cloud_upload.py
    @@ -8,11 +8,12 @@
     import sys
     import logging
     
    -from google.cloud import storage # Import for GCP
    -import boto3 # Import for AWS
    +from google.cloud import storage  # Import for GCP
    +import boto3  # Import for AWS
     
     logger = logging.getLogger(__name__)
     
    +
     def upload_to_gcp(bucket_name, source_file, destination_file):
         """Uploads a file to your GCP Bucket.
     
    @@ -31,7 +32,9 @@ def upload_to_gcp(bucket_name, source_file, destination_file):
         bucket = storage_client.bucket(bucket_name)
         blob = bucket.blob(destination_file)
         blob.upload_from_filename(source_file)
    -    logger.info('Upload of file {} as {} to {} successful'.format(source_file, destination_file, bucket_name))
    +    logger.info('Upload of file {} as {} to {} successful'.format(source_file, destination_file,
    +                                                                  bucket_name))
    +
     
     def upload_to_aws(bucket_name, source_file, destination_file):
         """Uploads a file to your AWS s3 bucket.
    @@ -48,23 +51,26 @@ def upload_to_aws(bucket_name, source_file, destination_file):
     
         s3 = boto3.client('s3')
         s3.upload_file(source_file, bucket_name, destination_file)
    -    logger.info('Upload of file {} as {} to {} successful'.format(source_file, destination_file, bucket_name))
    +    logger.info('Upload of file {} as {} to {} successful'.format(source_file, destination_file,
    +                                                                  bucket_name))
    +
     
     def main(argv):
         """Upload to AWS Example"""
     
         # Setting up logger for informational print statements.
         global logger
    -    level = logging.INFO # use logging.DEBUG for verbose details
    +    level = logging.INFO  # use logging.DEBUG for verbose details
         logging.basicConfig(level=level, format='%(message)s (%(filename)s:%(lineno)d)')
         logger = logging.getLogger()
     
         # Adjust the below parameters for your specific use case.
         # Optionally: Each of these parameters could also be set using argparse.
    -    aws_bucket_name = 'aws-imagedemo' # s3 bucket name
    -    source_file = 'requirements.txt' # test file to upload
    -    destination_file = source_file # using source_file name as destination_file name
    +    aws_bucket_name = 'aws-imagedemo'  # s3 bucket name
    +    source_file = 'requirements.txt'  # test file to upload
    +    destination_file = source_file  # using source_file name as destination_file name
         upload_to_aws(aws_bucket_name, source_file, destination_file)
     
    +
     if __name__ == '__main__':
         main(sys.argv[1:])
    diff --git a/python/examples/comms_test/Dockerfile b/python/examples/comms_test/Dockerfile
    index fc42db297..bc229a7e3 100644
    --- a/python/examples/comms_test/Dockerfile
    +++ b/python/examples/comms_test/Dockerfile
    @@ -3,8 +3,8 @@ FROM python:3.7-slim-buster
     RUN apt-get update && apt-get install -y iperf3 iputils-ping
     
     # Install the internal api wheels
    -COPY requirements.txt .
    -RUN python3 -m pip install -r requirements.txt
    +COPY docker-requirements.txt .
    +RUN python3 -m pip install -r docker-requirements.txt
     
     COPY . /app
     WORKDIR /app
    diff --git a/python/examples/comms_test/client.py b/python/examples/comms_test/client.py
    index ad85c6ce1..886aea77a 100644
    --- a/python/examples/comms_test/client.py
    +++ b/python/examples/comms_test/client.py
    @@ -37,22 +37,25 @@
     
     LOGGER = logging.getLogger(__name__)
     
    +
     def is_docker():
         path = '/proc/self/cgroup'
    -    return (
    -        os.path.exists('/.dockerenv') or
    -        os.path.isfile(path) and any('docker' in line for line in open(path)))
    +    return (os.path.exists('/.dockerenv') or
    +            os.path.isfile(path) and any('docker' in line for line in open(path)))
    +
     
     def _update_thread(async_task):
         while True:
             async_task.update()
             time.sleep(0.01)
     
    +
     def create_csv_filename(test_protocol):
         if is_docker():
             return f'/comms_out/comms_test_out_{test_protocol}{time.strftime("%Y%m%d-%H%M%S")}.csv'
         return f'comms_test_out_{test_protocol}{time.strftime("%Y%m%d-%H%M%S")}.csv'
     
    +
     def check_ping(server_hostname):
         cmd = ['ping', '-c', '1', '-w', '1', server_hostname]
         try:
    @@ -73,6 +76,8 @@ def check_ping(server_hostname):
         except Exception as e:
             print(e)
             return None
    +
    +
     class AsyncRobotState(AsyncPeriodicQuery):
         """Grab robot state."""
     
    @@ -83,16 +88,18 @@ def __init__(self, robot_state_client):
         def _start_query(self):
             return self._client.get_robot_state_async()
     
    +
     class AsyncMissionState(AsyncPeriodicQuery):
         """Grab mission state."""
     
         def __init__(self, mission_client):
             super(AsyncMissionState, self).__init__("mission_state", mission_client, LOGGER,
    -                                              period_sec=0.2)
    +                                                period_sec=0.2)
     
         def _start_query(self):
             return self._client.get_state_async()
     
    +
     def main(argv):
         parser = argparse.ArgumentParser()
         bosdyn.client.util.add_common_arguments(parser)
    @@ -108,7 +115,7 @@ def main(argv):
         options = parser.parse_args(argv)
     
         sdk = bosdyn.client.create_standard_sdk('CommsTestingClient', [MissionClient])
    -    robot = sdk.create_robot(options.hostname) #ROBOT_IP
    +    robot = sdk.create_robot(options.hostname)  #ROBOT_IP
         robot.authenticate(options.username, options.password)
         robot.sync_with_directory()
     
    @@ -179,14 +186,16 @@ def main(argv):
                                     'recv throughput(Mbps)': -1,
                                     'jitter(ms)': result.jitter_ms,
                                     'lost(%)': result.lost_percent,
    -                                'retransmits': -1})
    +                                'retransmits': -1
    +                            })
                             elif options.protocol == 'tcp':
                                 data_entry.update({
                                     'send throughput(Mbps)': result.sent_Mbps,
                                     'recv throughput(Mbps)': result.received_Mbps,
                                     'jitter(ms)': -1,
                                     'lost(%)': -1,
    -                                'retransmits': result.retransmits})
    +                                'retransmits': result.retransmits
    +                            })
                             data_list.append(data_entry)
                             print(data_list[-1])
     
    @@ -207,6 +216,7 @@ def main(argv):
             print("Caught KeyboardInterrupt, exiting")
             return True
     
    +
     if __name__ == '__main__':
         if not main(sys.argv[1:]):
             sys.exit(1)
    diff --git a/python/examples/comms_test/docker-requirements.txt b/python/examples/comms_test/docker-requirements.txt
    index 9a6b1f068..536c0f93f 100644
    --- a/python/examples/comms_test/docker-requirements.txt
    +++ b/python/examples/comms_test/docker-requirements.txt
    @@ -1,7 +1,7 @@
    -bosdyn-api==2.3.0
    -bosdyn-client==2.3.0
    -bosdyn-core==2.3.0
    -bosdyn-mission==2.3.0
    +bosdyn-api==3.0.0
    +bosdyn-client==3.0.0
    +bosdyn-core==3.0.0
    +bosdyn-mission==3.0.0
     certifi==2020.12.5
     chardet==4.0.0
     Deprecated==1.2.10
    diff --git a/python/examples/comms_test/requirements.txt b/python/examples/comms_test/requirements.txt
    index c1afc2ba7..1bac7e7bf 100644
    --- a/python/examples/comms_test/requirements.txt
    +++ b/python/examples/comms_test/requirements.txt
    @@ -1,4 +1,4 @@
     -f ../../../prebuilt
    -bosdyn-client ~= 2.0
    -bosdyn-mission ~= 2.0
    +bosdyn-client >= 2.0
    +bosdyn-mission >= 2.0
     iperf3==0.1.11
    \ No newline at end of file
    diff --git a/python/examples/data_acquisition_service/README.md b/python/examples/data_acquisition_service/README.md
    index e61e0cd56..3662f044a 100644
    --- a/python/examples/data_acquisition_service/README.md
    +++ b/python/examples/data_acquisition_service/README.md
    @@ -30,7 +30,6 @@ To run a plugin service from this example directory, issue the command:
     ```
     python3 {PLUGIN_FILE_NAME} --guid {GUID} --secret {SECRET} --host-ip {IP_WHERE_PLUGIN_WILL_RUN} --port {PORT_THE_PLUGIN_WILL_MONITOR} {ROBOT_IP}
     ```
    -Note: The pointcloud plugin will require you to pass the registered service name. You can find this by running `python -m bosdyn.client --username {USER} --password {PASSWORD} {ROBOT_IP} dir list`
     
     This example takes two different IP addresses as arguments. The `--host-ip` argument describes the IP address for the computer that will be running the data acquisition plugin service. A helper exists to try to determine the correct IP address. This command must be run on the same computer that will be running the plugin service:
     ```
    @@ -51,6 +50,25 @@ The network ports used by the services can be opened by running this command on
     sudo ufw allow {PORT_NUMBER}
     ```
     
    +### Point Cloud and Network Compute Bridge Plugins
    +The pointcloud and network compute bridge plugin will require you to pass the associated registered service name. You can find this by running:
    +```
    +python -m bosdyn.client --username {USER} --password {PASSWORD} {ROBOT_IP} dir list`
    +```
    +This will list all the services registered to the robot.  Use `--pointcloud-service` to provide the registered service name for pointcloud plugin.  Use `---worker-name` to provide the registered service name for network compute bridge plugin.
    +
    +For example, if a network compute bridge plugin is needed for a `fire-extinguisher-server`, the registered service name would show up in the directory list like the following:
    +```
    +name                                 type                                                      authority                                       tokens
    +----------------------------------------------------------------------------------------------------------------------------------------------------
    +fire-extinguisher-server             bosdyn.api.NetworkComputeBridgeWorker                     auth.spot.robot                                 user
    +```
    +
    +The command to start the network compute bridge pluging would be:
    +```
    +python3 {PLUGIN_FILE_NAME} --guid {GUID} --secret {SECRET} --host-ip {IP_WHERE_PLUGIN_WILL_RUN} --port {PORT_THE_PLUGIN_WILL_MONITOR} --worker-name fire-extinguisher-server {ROBOT_IP}
    +```
    +
     ## Testing the Data Acquisition Plugin
     
     There is a [plugin tester script](../tester_programs/README.md) that can be used while developing a new data acquisition plugin service to help ensure that the service can be communicated with and behaves as expected. The script runs through a series of tests checking the networking and the functionality of each of the plugin's RPCs.
    diff --git a/python/examples/data_acquisition_service/data_acquisition_download.py b/python/examples/data_acquisition_service/data_acquisition_download.py
    index 184d1c50f..84c0363ec 100644
    --- a/python/examples/data_acquisition_service/data_acquisition_download.py
    +++ b/python/examples/data_acquisition_service/data_acquisition_download.py
    @@ -50,27 +50,28 @@ def data_acquisition_download(config):
         except ValueError as val_err:
             print("Value Exception:\n" + str(val_err))
     
    -    download_data_REST(query_params, config.hostname, robot.user_token,
    -        config.destination_folder, config.additional_REST_params)
    +    download_data_REST(query_params, config.hostname, robot.user_token, config.destination_folder,
    +                       config.additional_REST_params)
     
     
     def main(argv):
         """Command line interface."""
         parser = argparse.ArgumentParser()
         bosdyn.client.util.add_common_arguments(parser)
    -    parser.add_argument(
    -        '--destination-folder', help=('The folder where the data should be downloaded to.'),
    -        required=False, default='.')
    +    parser.add_argument('--destination-folder',
    +                        help=('The folder where the data should be downloaded to.'), required=False,
    +                        default='.')
         parser.add_argument(
             '--query-from-timestamp', help=('The beginning timestamp to query from in RFC 3339 date'
    -        ' string format (YYYY-MM-DDTHH:MM::SSZ).'), required=True)
    +                                        ' string format (YYYY-MM-DDTHH:MM::SSZ).'), required=True)
         parser.add_argument(
             '--query-to-timestamp', help=('The end timestamp to query to in RFC 3339 date'
    -        ' string format (YYYY-MM-DDTHH:MM::SSZ).'), required=True)
    +                                      ' string format (YYYY-MM-DDTHH:MM::SSZ).'), required=True)
         parser.add_argument(
    -        '--additional-REST-params', type=json.loads, help=('JSON dictionary with additional REST '
    -        'parameters to append to the GET request when downloading the data. Parameters with '
    -        'multiple values need to be set to lists in the format (param: [value1, value2])'),
    +        '--additional-REST-params', type=json.loads,
    +        help=('JSON dictionary with additional REST '
    +              'parameters to append to the GET request when downloading the data. Parameters with '
    +              'multiple values need to be set to lists in the format (param: [value1, value2])'),
             required=False)
         options = parser.parse_args(argv)
     
    diff --git a/python/examples/data_acquisition_service/data_acquisition_example.py b/python/examples/data_acquisition_service/data_acquisition_example.py
    index 1ce8b8744..b8790ad7e 100644
    --- a/python/examples/data_acquisition_service/data_acquisition_example.py
    +++ b/python/examples/data_acquisition_service/data_acquisition_example.py
    @@ -22,7 +22,8 @@
     from bosdyn.client.data_acquisition import (DataAcquisitionClient, RequestIdDoesNotExistError)
     from bosdyn.client.data_acquisition_store import DataAcquisitionStoreClient
     from bosdyn.client.exceptions import ResponseError
    -from bosdyn.client.data_acquisition_helpers import (acquire_and_process_request, issue_acquire_data_request,
    +from bosdyn.client.data_acquisition_helpers import (acquire_and_process_request,
    +                                                    issue_acquire_data_request,
                                                         cancel_acquisition_request, download_data_REST,
                                                         make_time_query_params)
     
    @@ -55,29 +56,30 @@ def data_acquisition(config):
         acquisition_requests = data_acquisition_pb2.AcquisitionRequestList()
         acquisition_requests.image_captures.extend([
             data_acquisition_pb2.ImageSourceCapture(image_service="image",
    -                                                image_source="back_fisheye_image")])
    +                                                image_source="back_fisheye_image")
    +    ])
         acquisition_requests.data_captures.extend([
             data_acquisition_pb2.DataCapture(name="robot-state"),
             data_acquisition_pb2.DataCapture(name="detailed-position-data"),
    -        data_acquisition_pb2.DataCapture(name="detected-objects")])
    +        data_acquisition_pb2.DataCapture(name="detected-objects")
    +    ])
     
    -    acquire_and_process_request(data_acq_client, acquisition_requests,
    -                                group_name, "InternalAcquisitions")
    +    acquire_and_process_request(data_acq_client, acquisition_requests, group_name,
    +                                "InternalAcquisitions")
     
         # Request 2 contains capture requests for all the capabilities and a user-generated metadata
         # struct.
         json_struct = Struct()
    -    json_struct.update({
    -        "user_comment" : "it is raining"
    -    })
    -    metadata=data_acquisition_pb2.Metadata(data=json_struct)
    +    json_struct.update({"user_comment": "it is raining"})
    +    metadata = data_acquisition_pb2.Metadata(data=json_struct)
     
         acquisition_requests = data_acquisition_pb2.AcquisitionRequestList()
    -    acquisition_requests.image_captures.extend(
    -        [data_acquisition_pb2.ImageSourceCapture(image_service="image",
    -            image_source="left_fisheye_image"),
    +    acquisition_requests.image_captures.extend([
             data_acquisition_pb2.ImageSourceCapture(image_service="image",
    -            image_source="right_fisheye_image")])
    +                                                image_source="left_fisheye_image"),
    +        data_acquisition_pb2.ImageSourceCapture(image_service="image",
    +                                                image_source="right_fisheye_image")
    +    ])
         acquisition_requests.data_captures.extend([
             data_acquisition_pb2.DataCapture(name="robot-state"),
             data_acquisition_pb2.DataCapture(name="detailed-position-data"),
    @@ -85,12 +87,11 @@ def data_acquisition(config):
             data_acquisition_pb2.DataCapture(name="detected-objects"),
             data_acquisition_pb2.DataCapture(name="GPS"),
             data_acquisition_pb2.DataCapture(name="velodyne-point-cloud")
    -        ])
    +    ])
     
         acquire_and_process_request(data_acq_client, acquisition_requests, group_name,
                                     "AllAcquisitions", metadata)
     
    -
         # Request 3 contains capture requests for only one capability from main DAQ and one capability
         # from DAQ plugin.
         acquisition_requests = data_acquisition_pb2.AcquisitionRequestList()
    @@ -98,12 +99,11 @@ def data_acquisition(config):
             data_acquisition_pb2.DataCapture(name="robot-state"),
             data_acquisition_pb2.DataCapture(name="GPS"),
             data_acquisition_pb2.DataCapture(name="velodyne-point-cloud"),
    -        ])
    +    ])
     
         acquire_and_process_request(data_acq_client, acquisition_requests, group_name,
                                     "PartialAcquisitions")
     
    -
         # Request #4 shows how to issue and then cancel different data acquisition requests (one on-robot
         # data source and one off-robot plugin data source).
         print("\n-----------------------------------")
    @@ -112,29 +112,29 @@ def data_acquisition(config):
             data_acquisition_pb2.DataCapture(name="robot-state"),
             data_acquisition_pb2.DataCapture(name="slow-gps"),
         ])
    -    request_id, action_id = issue_acquire_data_request(
    -        data_acq_client, acquisition_requests, group_name, "AcquisitionsToCancel")
    +    request_id, action_id = issue_acquire_data_request(data_acq_client, acquisition_requests,
    +                                                       group_name, "AcquisitionsToCancel")
         time.sleep(2)
         cancel_acquisition_request(data_acq_client, request_id)
     
    -
         # Request 5 contains a SpotCAM capture request.
         acquisition_requests = data_acquisition_pb2.AcquisitionRequestList()
         acquisition_requests.data_captures.extend([
             data_acquisition_pb2.DataCapture(name="spot-cam-pano"),
             data_acquisition_pb2.DataCapture(name="spot-cam-ptz"),
             data_acquisition_pb2.DataCapture(name="spot-cam-ir"),
    -        data_acquisition_pb2.DataCapture(name="robot-state")])
    +        data_acquisition_pb2.DataCapture(name="robot-state")
    +    ])
     
         acquire_and_process_request(data_acq_client, acquisition_requests, group_name,
                                     "SpotCAMAcquisitions")
     
         # Get the end time, and download all the data from the example.
         end_time_secs = time.time()
    -    # Add a buffer around the start/end time for downloading.
    -    query_params = make_time_query_params(start_time_secs-3.0, end_time_secs+3.0, robot)
    +    query_params = make_time_query_params(start_time_secs, end_time_secs, robot)
         download_data_REST(query_params, config.hostname, robot.user_token, destination_folder='.')
     
    +
     def main(argv):
         """Command line interface."""
         parser = argparse.ArgumentParser()
    diff --git a/python/examples/data_acquisition_service/gps_metadata_plugin/docker-requirements.txt b/python/examples/data_acquisition_service/gps_metadata_plugin/docker-requirements.txt
    index 5ba447039..40fd92f98 100644
    --- a/python/examples/data_acquisition_service/gps_metadata_plugin/docker-requirements.txt
    +++ b/python/examples/data_acquisition_service/gps_metadata_plugin/docker-requirements.txt
    @@ -1,6 +1,6 @@
    -bosdyn-api==2.3.0
    -bosdyn-client==2.3.0
    -bosdyn-core==2.3.0
    +bosdyn-api==3.0.0
    +bosdyn-client==3.0.0
    +bosdyn-core==3.0.0
     certifi==2020.12.5
     chardet==4.0.0
     Deprecated==1.2.10
    diff --git a/python/examples/data_acquisition_service/gps_metadata_plugin/gps_metadata_plugin_service.py b/python/examples/data_acquisition_service/gps_metadata_plugin/gps_metadata_plugin_service.py
    index cf4a80fb8..0e966df23 100644
    --- a/python/examples/data_acquisition_service/gps_metadata_plugin/gps_metadata_plugin_service.py
    +++ b/python/examples/data_acquisition_service/gps_metadata_plugin/gps_metadata_plugin_service.py
    @@ -19,7 +19,8 @@
     from bosdyn.client.directory_registration import (DirectoryRegistrationClient,
                                                       DirectoryRegistrationKeepAlive)
     import bosdyn.client.util
    -from bosdyn.client.util import GrpcServiceRunner, setup_logging
    +from bosdyn.client.util import setup_logging
    +from bosdyn.client.server_util import GrpcServiceRunner
     
     DIRECTORY_NAME = 'data-acquisition-gps-metadata-plugin'
     AUTHORITY = 'data-acquisition-gps-metadata-plugin'
    @@ -33,13 +34,16 @@
     
     
     class GPSData:
    +
         def __init__(self, lat, lon, alt):
             self.lat = lat
             self.lon = lon
             self.alt = alt
     
    +
     class GPS_Adapter:
         """Provide access to the latest data from a fake sensor."""
    +
         def __init__(self):
             self.data_lock = threading.Lock()
             self.data = None
    @@ -56,8 +60,7 @@ def read_GPS_from_sensor(self):
             """Update the internal data with random numbers."""
             with self.data_lock:
                 self.data = GPSData(random.random() * math.pi - math.pi / 2,
    -                                random.random() * 2 * math.pi - math.pi,
    -                                0.0)
    +                                random.random() * 2 * math.pi - math.pi, 0.0)
     
         def get_GPS_data(self, request, store_helper):
             """Save the latest GPS data to the data store."""
    diff --git a/python/examples/data_acquisition_service/gps_metadata_plugin/requirements.txt b/python/examples/data_acquisition_service/gps_metadata_plugin/requirements.txt
    index c5bfed558..44bd10089 100644
    --- a/python/examples/data_acquisition_service/gps_metadata_plugin/requirements.txt
    +++ b/python/examples/data_acquisition_service/gps_metadata_plugin/requirements.txt
    @@ -1,4 +1,4 @@
     -f ../../../../prebuilt
     
    -bosdyn-client ~= 2.2
    +bosdyn-client >= 2.2
     Pillow==6.1.0
    \ No newline at end of file
    diff --git a/python/examples/data_acquisition_service/network_compute_bridge_plugin/Dockerfile b/python/examples/data_acquisition_service/network_compute_bridge_plugin/Dockerfile
    new file mode 100644
    index 000000000..aaf046c49
    --- /dev/null
    +++ b/python/examples/data_acquisition_service/network_compute_bridge_plugin/Dockerfile
    @@ -0,0 +1,9 @@
    +FROM python:3.7-slim
    +
    +COPY docker-requirements.txt .
    +RUN python3 -m pip install -r docker-requirements.txt
    +
    +COPY network_compute_bridge_plugin_service.py /app/network_compute_bridge_plugin_service.py
    +WORKDIR /app
    +
    +ENTRYPOINT ["python3", "/app/network_compute_bridge_plugin_service.py"]
    diff --git a/python/examples/data_acquisition_service/network_compute_bridge_plugin/docker-requirements.txt b/python/examples/data_acquisition_service/network_compute_bridge_plugin/docker-requirements.txt
    new file mode 100644
    index 000000000..6f53e24d3
    --- /dev/null
    +++ b/python/examples/data_acquisition_service/network_compute_bridge_plugin/docker-requirements.txt
    @@ -0,0 +1,4 @@
    +bosdyn-api==3.0.0
    +bosdyn-client==3.0.0
    +bosdyn-core==3.0.0
    +Pillow==6.1.0
    diff --git a/python/examples/data_acquisition_service/network_compute_bridge_plugin/network_compute_bridge_plugin_service.py b/python/examples/data_acquisition_service/network_compute_bridge_plugin/network_compute_bridge_plugin_service.py
    new file mode 100644
    index 000000000..7d6963718
    --- /dev/null
    +++ b/python/examples/data_acquisition_service/network_compute_bridge_plugin/network_compute_bridge_plugin_service.py
    @@ -0,0 +1,285 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +from __future__ import print_function
    +
    +import logging
    +import time
    +import google.protobuf.json_format as json_format
    +
    +from bosdyn.api import data_acquisition_pb2
    +from bosdyn.api import data_acquisition_plugin_service_pb2_grpc
    +from bosdyn.api import network_compute_bridge_pb2
    +from bosdyn.api import world_object_pb2
    +from bosdyn.client.data_acquisition import DataAcquisitionClient
    +from bosdyn.client.data_acquisition_plugin_service import (Capability, DataAcquisitionPluginService,
    +                                                           make_error, RequestCancelledError)
    +from bosdyn.client.directory_registration import (DirectoryRegistrationClient,
    +                                                  DirectoryRegistrationKeepAlive)
    +from bosdyn.client.network_compute_bridge_client import (NetworkComputeBridgeClient,
    +                                                         ExternalServiceNotFoundError,
    +                                                         ExternalServerError)
    +import bosdyn.client.util
    +from bosdyn.client.util import setup_logging, GrpcServiceRunner
    +# from bosdyn.client.server_util import 
    +
    +
    +AUTHORITY = 'data-acquisition-ncb-plugin'
    +
    +_LOGGER = logging.getLogger(__name__)
    +
    +kCapabilityImage = "image"
    +kCapabilityObjectInImage = "object_in_image"
    +kCapabilityNames = [kCapabilityImage, kCapabilityObjectInImage]
    +
    +def get_directory_name(network_compute_bridge_worker_name):
    +    return '{}-{}-plugin'.format(
    +        DataAcquisitionClient.default_service_name, network_compute_bridge_worker_name)
    +
    +class NetworkComputeBrideAdapter:
    +    """Provide access to the latest data from a network compute bridge worker.
    +
    +    Args:
    +        robot (bosdyn.client.Robot): Robot instance to use for creating NetworkComputeBridgeClient.
    +        network_compute_bridge_worker_name (string): Name as listed in the directory of the worker.
    +    """
    +
    +    def __init__(self, robot, network_compute_bridge_worker_name):
    +        self._network_compute_bridge_client = robot.ensure_client(NetworkComputeBridgeClient.default_service_name)
    +        self._worker_name = network_compute_bridge_worker_name
    +
    +    def get_capabilities(self):
    +        """Get list of available data capture options for the network compute bridge worker.
    +
    +        Returns:
    +            Array with list of Capabilities corresponding to the available data capture options.
    +        """
    +
    +        # Try to get a list of models available from the worker to see if this service is alive.
    +        while True:
    +            try:
    +                server_data = network_compute_bridge_pb2.NetworkComputeServerConfiguration(
    +                    service_name=self._worker_name)
    +                list_req = network_compute_bridge_pb2.ListAvailableModelsRequest(
    +                    server_config=server_data)
    +                response = self._network_compute_bridge_client.list_available_models_command(list_req)
    +                break
    +            except (ExternalServiceNotFoundError, ExternalServerError):
    +                _LOGGER.exception('Network compute bridge worker is still unavailable:\n')
    +                time.sleep(2)
    +        if response.header.error.message:
    +            _LOGGER.error("List available models from %s returned with error: %s",
    +                          self._worker_name, response.header.error.message)
    +        else:
    +            _LOGGER.info('Available models from %s:', self._worker_name)
    +            for model in response.available_models:
    +                _LOGGER.info('   %s', model)
    +        
    +        # Compose the list of data capture options.
    +        capabilities = []
    +        _LOGGER.info('Available data capture options:')
    +        for name in kCapabilityNames:
    +            _LOGGER.info('   %s', name)
    +            capabilities.append(
    +                Capability(name=name,
    +                           description="Processed {} from {}.".format(name, self._worker_name),
    +                           channel_name="{}--{}".format(self._worker_name, name)))
    +
    +        return capabilities
    +
    +    def get_network_compute_data(self, request, store_helper):
    +        """Make the RPC to the network compute bridge worker and save results to the data store.
    +
    +        Args:
    +            request (bosdyn.api.AcquirePluginDataRequest): Plugin request.
    +            store_helper (bosdyn.client.DataAcquisitionStoreHelper): Helper used to manage storage
    +                of objects in data acquisition store service.
    +        """
    +
    +        try:
    +            _LOGGER.info("Requesting data from %s...", self._worker_name)
    +            network_compute_bridge_metadata = request.metadata.data["network_compute_bridge"]
    +            response = self._request_data(request, network_compute_bridge_metadata, store_helper)
    +            
    +            for capture in request.acquisition_requests.data_captures:
    +                data_id = self._get_data_id(request, capture.name)
    +
    +                if capture.name == kCapabilityImage:
    +                    store_helper.cancel_check()
    +                    _LOGGER.info("Storing image with data id %s...", data_id)
    +                    store_helper.state.set_status(data_acquisition_pb2.GetStatusResponse.STATUS_SAVING)
    +                    store_helper.store_image(response.image_response.shot, data_id)
    +                    
    +                elif capture.name == kCapabilityObjectInImage:
    +                    store_helper.cancel_check()
    +                    _LOGGER.info("Storing detection info with data id %s...", data_id)
    +
    +                    detection_info = world_object_pb2.ListWorldObjectResponse(
    +                        world_objects=response.object_in_image)
    +                    detection_metadata = data_acquisition_pb2.Metadata()
    +                    detection_metadata.data.update(json_format.MessageToDict(detection_info))
    +                    reference_id = self._get_data_id(request, kCapabilityImage)
    +                    associated_metadata = data_acquisition_pb2.AssociatedMetadata(
    +                        reference_id=reference_id, metadata=detection_metadata)
    +                    store_helper.store_metadata(associated_metadata, data_id)
    +                else:
    +                    errMsg = "Unknown capability {}.".format(capture.name)
    +                    store_helper.state.add_errors([make_error(data_id, errMsg)])
    +                    _LOGGER.error(errMsg)
    +
    +        except ValueError:
    +            errMsg = "Unable to get network compute bridge info."
    +            store_helper.state.add_errors([make_error(data_id, errMsg)])
    +            _LOGGER.error(errMsg)
    +        except RequestCancelledError:
    +            _LOGGER.info("Capture canceled.")
    +
    +    def _get_data_id(self, request, capability_name):
    +        """Get a data id for the given capability.
    +
    +        Args:
    +            request (bosdyn.api.AcquirePluginDataRequest): Plugin request.
    +            capability_name (string):  Name of the capability to get data id for.
    +        
    +        Returns:
    +            The data id associated with the given capability name.
    +        """
    +        data_id = data_acquisition_pb2.DataIdentifier(
    +            action_id=request.action_id,
    +            channel="{}--{}".format(self._worker_name, capability_name))
    +        return data_id
    +
    +    def _request_data(self, request, network_compute_bridge_metadata, store_helper):
    +        """Make the RPC to the network compute bridge worker.
    +
    +        Args:
    +            request (bosdyn.api.AcquirePluginDataRequest): Plugin request.
    +            network_compute_bridge_metadata (google.protobuf.Struct): Metadata containing 
    +                information needed for the request
    +            store_helper (bosdyn.client.DataAcquisitionStoreHelper): Helper used to manage storage
    +                of objects in data acquisition store service.
    +
    +        Returns:
    +            The response from the compute request or None if error occurs
    +        """
    +
    +        server_config = network_compute_bridge_pb2.NetworkComputeServerConfiguration(
    +            service_name=self._worker_name)
    +
    +        try:
    +            image_service = network_compute_bridge_metadata["image_service"]
    +            image_source = network_compute_bridge_metadata["image_source"]
    +        except ValueError:
    +            errMsg = "Unable to get image service and source info."
    +            data_id = self._get_data_id(request, kCapabilityImage)
    +            store_helper.state.add_errors([make_error(data_id, errMsg)])
    +            _LOGGER.error(errMsg)
    +
    +        service_source = network_compute_bridge_pb2.ImageSourceAndService(
    +            image_service=image_service, image_source=image_source)
    +
    +        try:
    +            model_name = network_compute_bridge_metadata["model_name"]
    +            min_confidence = network_compute_bridge_metadata["min_confidence"]
    +        except ValueError:
    +            errMsg = "Unable to get model name or confidence value."
    +            data_id = self._get_data_id(request, kCapabilityObjectInImage)
    +            store_helper.state.add_errors([make_error(data_id, errMsg)])
    +            _LOGGER.error(errMsg)
    +
    +        input_data = network_compute_bridge_pb2.NetworkComputeInputData(
    +            image_source_and_service=service_source, model_name=model_name,
    +            min_confidence=min_confidence)
    +        network_compute_request = network_compute_bridge_pb2.NetworkComputeRequest(
    +            server_config=server_config, input_data=input_data)
    +
    +        response = self._network_compute_bridge_client.network_compute_bridge_command(
    +            network_compute_request)
    +        return response
    +
    +
    +def make_service(robot, network_compute_bridge_worker_name, logger=None):
    +    adapter = NetworkComputeBrideAdapter(robot, network_compute_bridge_worker_name)
    +    capabilities = adapter.get_capabilities()
    +    return DataAcquisitionPluginService(robot, capabilities, adapter.get_network_compute_data)
    +
    +
    +def run_service(bosdyn_sdk_robot, port, network_compute_bridge_worker_name, logger=None):
    +    # Proto service specific function used to attach a servicer to a server.
    +    add_servicer_to_server_fn = data_acquisition_plugin_service_pb2_grpc.add_DataAcquisitionPluginServiceServicer_to_server
    +
    +    # Instance of the servicer to be run.
    +    service_servicer = make_service(bosdyn_sdk_robot, network_compute_bridge_worker_name, logger=logger)
    +    return GrpcServiceRunner(service_servicer, add_servicer_to_server_fn, port, logger=logger)
    +
    +
    +def add_network_compute_bridge_plugin_arguments(parser):
    +    parser.add_argument('--worker-name',
    +                        help="Name of the network compute bridge worker to get data from.", required=True)
    +
    +
    +def get_guid_and_secret():
    +    # Returns the GUID and secret on the Spot CORE
    +    kGuidAndSecretPath = '/opt/payload_credentials/payload_guid_and_secret'
    +    try:
    +        payload_file = open(kGuidAndSecretPath)
    +        guid = payload_file.readline().strip('\n')
    +        secret = payload_file.readline().strip('\n')
    +    except IOError as io_error:
    +        print("Unable to get the GUID/Secret for Spot Core: IOError when reading the file at: "+kGuidAndSecretPath)
    +        raise io_error
    +    return guid, secret
    +
    +
    +if __name__ == '__main__':
    +    # Define all arguments used by this service.
    +    import argparse
    +    parser = argparse.ArgumentParser()
    +    bosdyn.client.util.add_base_arguments(parser)
    +    default_port = 50052
    +    parser.add_argument('-p', '--port', help=f'Server\'s port number, default: {default_port}',
    +                        default=default_port)
    +    parser.add_argument('--on-spot-core', help='Use SpotCore GUID to register the server with Spot', action='store_true')
    +    parser.add_argument('--guid', help='Unique GUID of the payload, new one will be generated if none specified')
    +    parser.add_argument('--secret', help='Secret of the payload, new one will be generated if none specified')
    +    add_network_compute_bridge_plugin_arguments(parser)
    +    options = parser.parse_args()
    +
    +    # Setup logging to use either INFO level or DEBUG level.
    +    setup_logging(options.verbose)
    +
    +    # Create and authenticate a bosdyn robot object.
    +    self_ip = bosdyn.client.common.get_self_ip(options.hostname)
    +    print('Detected IP address as: ' + self_ip)
    +    sdk = bosdyn.client.create_standard_sdk("PointcloudPluginServiceSDK")
    +    robot = sdk.create_robot(options.hostname)
    +
    +    if options.on_spot_core:
    +        guid, secret = get_guid_and_secret()
    +        robot.authenticate_from_payload_credentials(guid, secret)
    +    else:
    +        if not options.guid or not options.secret:
    +            _LOGGER.error('GUID and secret need to both be specified.')
    +            exit(1)
    +        else:
    +            robot.authenticate_from_payload_credentials(options.guid, options.secret)
    +    robot.sync_with_directory()
    +
    +    # Create a service runner to start and maintain the service on background thread.
    +    service_runner = run_service(robot, options.port, options.worker_name, logger=_LOGGER)
    +
    +    # Set up the directory name.  The name must have the pattern data-acquisition-XXX-plugin.
    +    directory_name = get_directory_name(options.worker_name)
    +
    +    # Use a keep alive to register the service with the robot directory.
    +    dir_reg_client = robot.ensure_client(DirectoryRegistrationClient.default_service_name)
    +    keep_alive = DirectoryRegistrationKeepAlive(dir_reg_client, logger=_LOGGER)
    +    keep_alive.start(directory_name, DataAcquisitionPluginService.service_type, AUTHORITY,
    +                     self_ip, service_runner.port)
    +
    +    # Attach the keep alive to the service runner and run until a SIGINT is received.
    +    with keep_alive:
    +        service_runner.run_until_interrupt()
    \ No newline at end of file
    diff --git a/python/examples/data_acquisition_service/network_compute_bridge_plugin/requirements.txt b/python/examples/data_acquisition_service/network_compute_bridge_plugin/requirements.txt
    new file mode 100644
    index 000000000..44bd10089
    --- /dev/null
    +++ b/python/examples/data_acquisition_service/network_compute_bridge_plugin/requirements.txt
    @@ -0,0 +1,4 @@
    +-f ../../../../prebuilt
    +
    +bosdyn-client >= 2.2
    +Pillow==6.1.0
    \ No newline at end of file
    diff --git a/python/examples/data_acquisition_service/piksi_gps_plugin/docker-requirements.txt b/python/examples/data_acquisition_service/piksi_gps_plugin/docker-requirements.txt
    index 9c29b75f1..dafa81b54 100644
    --- a/python/examples/data_acquisition_service/piksi_gps_plugin/docker-requirements.txt
    +++ b/python/examples/data_acquisition_service/piksi_gps_plugin/docker-requirements.txt
    @@ -1,6 +1,6 @@
    -bosdyn-api==2.3.0
    -bosdyn-client==2.3.0
    -bosdyn-core==2.3.0
    +bosdyn-api==3.0.0
    +bosdyn-client==3.0.0
    +bosdyn-core==3.0.0
     certifi==2020.12.5
     chardet==3.0.4
     construct==2.9.33
    @@ -16,7 +16,7 @@ PyJWT==2.0.1
     pylibftdi==0.19.0
     pyserial==3.5
     python-rapidjson==0.9.1
    -pyusb==1.1.1
    +pyusb==1.2.0
     requests==2.20.1
     sbp==3.4.5
     six==1.12.0
    diff --git a/python/examples/data_acquisition_service/piksi_gps_plugin/piksi_metadata_plugin_service.py b/python/examples/data_acquisition_service/piksi_gps_plugin/piksi_metadata_plugin_service.py
    index 8fc9280e9..31a3e041e 100644
    --- a/python/examples/data_acquisition_service/piksi_gps_plugin/piksi_metadata_plugin_service.py
    +++ b/python/examples/data_acquisition_service/piksi_gps_plugin/piksi_metadata_plugin_service.py
    @@ -19,7 +19,8 @@
                                                       DirectoryRegistrationKeepAlive)
     from sbp.client.drivers.pyserial_driver import PySerialDriver
     import bosdyn.client.util
    -from bosdyn.client.util import GrpcServiceRunner, setup_logging
    +from bosdyn.client.util import setup_logging
    +from bosdyn.client.server_util import GrpcServiceRunner
     
     from sbp.client import Handler, Framer
     from sbp.navigation import SBP_MSG_POS_LLH_DEP_A, SBP_MSG_POS_LLH
    @@ -34,14 +35,18 @@
     kDescription = 'Piksi GPS latitude/longitude coordinates'
     kCapabilities = [Capability(name=kName, description=kDescription, channel_name=kChannel)]
     
    +
     class GPSData:
    +
         def __init__(self, lat, lon, alt):
             self.lat = lat
             self.lon = lon
             self.alt = alt
     
    +
     class GPS_Adapter:
         """Provide access to the latest data from a fake sensor."""
    +
         def __init__(self):
             self.data_lock = threading.Lock()
             self.data = None
    diff --git a/python/examples/data_acquisition_service/piksi_gps_plugin/requirements.txt b/python/examples/data_acquisition_service/piksi_gps_plugin/requirements.txt
    index 3fb51c8e6..7d80675cf 100644
    --- a/python/examples/data_acquisition_service/piksi_gps_plugin/requirements.txt
    +++ b/python/examples/data_acquisition_service/piksi_gps_plugin/requirements.txt
    @@ -1,5 +1,5 @@
     -f ../../../../prebuilt
     
    -bosdyn-client ~= 2.2
    +bosdyn-client >= 2.2
     Pillow==6.1.0
     sbp
    \ No newline at end of file
    diff --git a/python/examples/data_acquisition_service/pointcloud_plugin/docker-requirements.txt b/python/examples/data_acquisition_service/pointcloud_plugin/docker-requirements.txt
    index 5ba447039..40fd92f98 100644
    --- a/python/examples/data_acquisition_service/pointcloud_plugin/docker-requirements.txt
    +++ b/python/examples/data_acquisition_service/pointcloud_plugin/docker-requirements.txt
    @@ -1,6 +1,6 @@
    -bosdyn-api==2.3.0
    -bosdyn-client==2.3.0
    -bosdyn-core==2.3.0
    +bosdyn-api==3.0.0
    +bosdyn-client==3.0.0
    +bosdyn-core==3.0.0
     certifi==2020.12.5
     chardet==4.0.0
     Deprecated==1.2.10
    diff --git a/python/examples/data_acquisition_service/pointcloud_plugin/pointcloud_plugin_service.py b/python/examples/data_acquisition_service/pointcloud_plugin/pointcloud_plugin_service.py
    index 14a083166..503d1f750 100644
    --- a/python/examples/data_acquisition_service/pointcloud_plugin/pointcloud_plugin_service.py
    +++ b/python/examples/data_acquisition_service/pointcloud_plugin/pointcloud_plugin_service.py
    @@ -19,7 +19,8 @@
     from bosdyn.client.exceptions import ServiceUnavailableError
     from bosdyn.client.point_cloud import PointCloudClient
     import bosdyn.client.util
    -from bosdyn.client.util import GrpcServiceRunner, setup_logging
    +from bosdyn.client.util import setup_logging
    +from bosdyn.client.server_util import GrpcServiceRunner
     
     DIRECTORY_NAME = 'data-acquisition-pointcloud-plugin'
     AUTHORITY = 'data-acquisition-pc-plugin'
    @@ -40,8 +41,8 @@ def __init__(self, robot, point_cloud_service_name):
     
         def get_capabilities(self):
             """Get list of available capabilities to capture point-clouds.
    -        
    -        The capabilities available will vary depending on what sources of pointclouds are 
    +
    +        The capabilities available will vary depending on what sources of pointclouds are
             available. This method blocks until it communicates with the SpotCAM MediaLog service.
     
             Returns:
    @@ -60,10 +61,9 @@ def get_capabilities(self):
             for source in sources:
                 name = source.name
                 capabilities.append(
    -                Capability(
    -                    name=name,
    -                    description="Point-clouds from a {} LIDAR sensor.".format(name),
    -                    channel_name="{}--{}".format(self._service_name, name)))
    +                Capability(name=name,
    +                           description="Point-clouds from a {} LIDAR sensor.".format(name),
    +                           channel_name="{}--{}".format(self._service_name, name)))
             return capabilities
     
         def get_point_cloud_data(self, request, store_helper):
    diff --git a/python/examples/data_acquisition_service/pointcloud_plugin/requirements.txt b/python/examples/data_acquisition_service/pointcloud_plugin/requirements.txt
    index c5bfed558..44bd10089 100644
    --- a/python/examples/data_acquisition_service/pointcloud_plugin/requirements.txt
    +++ b/python/examples/data_acquisition_service/pointcloud_plugin/requirements.txt
    @@ -1,4 +1,4 @@
     -f ../../../../prebuilt
     
    -bosdyn-client ~= 2.2
    +bosdyn-client >= 2.2
     Pillow==6.1.0
    \ No newline at end of file
    diff --git a/python/examples/data_buffer/README.md b/python/examples/data_buffer/README.md
    new file mode 100644
    index 000000000..4c097ab70
    --- /dev/null
    +++ b/python/examples/data_buffer/README.md
    @@ -0,0 +1,52 @@
    +
    +
    +# Using the Data Buffer service
    +
    +The Data Buffer service can be used to log data to the robot which can later be retrieved via API calls to the [Data Service](../data_service/README.md) or by [bddf-download](../bddf_download/README.md).  This example shows how to log various kinds of data to robot.
    +
    +
    +## Setup Dependencies
    +
    +This example requires the bosdyn API and client to be installed, and must be run using python3. Using pip, these dependencies can be installed using:
    +```
    +python3 -m pip install -r requirements.txt
    +```
    +
    +## Running the Example
    +
    +The example requires that you specify a username and password to authenticate with the robot.
    +
    +### Add an 'operator comment' to the log
    +
    +This example adds an operator comment to the robot log.  This comment will show up in the comments list in the robot log download web page.
    +```
    +python3 data_buffer.py HOSTNAME --username USERNAME --password PASSWORD \
    +    operator 'This is a test of the Data Buffer client.'
    +```
    +
    +### Add 'blob' data to the log
    +
    +This example adds a serialized protobuf to the log using the `add_blob()` method of the Data Buffer client.
    +```
    +python3 data_buffer.py HOSTNAME --username USERNAME --password PASSWORD blob
    +```
    +
    +### Add 'protobuf' data to the log
    +
    +This is similar to the 'blob' data example, but it serializes the protobuf automatically and sets the message type from the full protobuf type name.
    +```
    +python3 data_buffer.py HOSTNAME --username USERNAME --password PASSWORD protobuf
    +```
    +
    +### Add an event to the log
    +
    +This writes an example event into the log.
    +```
    +python3 data_buffer.py HOSTNAME --username USERNAME --password PASSWORD event
    +```
    diff --git a/python/examples/data_buffer/data_buffer.py b/python/examples/data_buffer/data_buffer.py
    new file mode 100644
    index 000000000..88af18993
    --- /dev/null
    +++ b/python/examples/data_buffer/data_buffer.py
    @@ -0,0 +1,118 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""Tutorial to show how to use the Boston Dynamics API to log data."""
    +import argparse
    +import sys
    +import time
    +
    +import bosdyn.api.data_buffer_pb2 as data_buffer_protos
    +from bosdyn.api.parameter_pb2 import Parameter
    +import bosdyn.client
    +from bosdyn.client.data_buffer import DataBufferClient
    +import bosdyn.client.util
    +
    +
    +def add_blob(robot, options):
    +    """Add binary data to the log."""
    +
    +    client = robot.ensure_client(DataBufferClient.default_service_name)
    +    robot.time_sync.wait_for_sync()
    +    robot_timestamp = robot.time_sync.robot_timestamp_from_local_secs(time.time())
    +
    +    # create a text message proto, just to have something to store.
    +    msg = data_buffer_protos.TextMessage(
    +        message='test message',
    +        timestamp=robot_timestamp,
    +        source='test-source',
    +        level=data_buffer_protos.TextMessage.LEVEL_INFO,  # pylint: disable=no-member
    +        tag='test')
    +
    +    typename = msg.DESCRIPTOR.full_name
    +    client.add_blob(msg.SerializeToString(), type_id=typename, channel=typename,
    +                    robot_timestamp=robot_timestamp, write_sync=options.write_sync)
    +    print("Added message blob.")
    +
    +
    +def add_protobuf(robot, options):
    +    """Add protobuf data to the log."""
    +
    +    client = robot.ensure_client(DataBufferClient.default_service_name)
    +    robot.time_sync.wait_for_sync()
    +    robot_timestamp = robot.time_sync.robot_timestamp_from_local_secs(time.time())
    +
    +    # create a text message proto, just to have something to store.
    +    msg = data_buffer_protos.TextMessage(
    +        message='test protobuf',
    +        timestamp=robot_timestamp,
    +        source=robot.client_name,
    +        level=data_buffer_protos.TextMessage.LEVEL_INFO,  # pylint: disable=no-member
    +        tag='test')
    +
    +    client.add_protobuf(msg, robot_timestamp=robot_timestamp, write_sync=options.write_sync)
    +    print("Added protobuf message.")
    +
    +
    +def add_event(robot):
    +    """Record an event in the log."""
    +
    +    robot.time_sync.wait_for_sync()
    +    # pylint: disable=no-member
    +    robot.log_event('examples:example_event', level=data_buffer_protos.Event.LEVEL_LOW,
    +                    description='This is an example event from demonstrating the API',
    +                    start_timestamp_secs=time.time(), parameters=[
    +                        Parameter(label='test:length', units='m', float_value=3.141),
    +                        Parameter(label='test:boolean', bool_value=True)
    +                    ])
    +    print("Added event.")
    +
    +
    +def main():
    +    """Command line interface."""
    +    parser = argparse.ArgumentParser()
    +    bosdyn.client.util.add_common_arguments(parser)
    +
    +    subparsers = parser.add_subparsers(help='commands', dest='command')
    +    operator_parser = subparsers.add_parser('operator', help='add operator comment')
    +    operator_parser.add_argument('message', help='operator comment message')
    +
    +    blob_parser = subparsers.add_parser('blob', help='write a blob to the log')
    +    blob_parser.add_argument('--write-sync', action='store_true',
    +                             help='ensure data is on disk before returning')
    +
    +    protobuf_parser = subparsers.add_parser('protobuf', help='serialize a protobuf to the log')
    +    protobuf_parser.add_argument('--write-sync', action='store_true',
    +                                 help='ensure data is on disk before returning')
    +
    +    _event_parser = subparsers.add_parser('event', help='add an event to the log')
    +
    +    options = parser.parse_args()
    +
    +    bosdyn.client.util.setup_logging(options.verbose)
    +
    +    sdk = bosdyn.client.create_standard_sdk('DataBufferClientExample')
    +    robot = sdk.create_robot(options.hostname)
    +    robot.authenticate(options.username, options.password)
    +
    +    if options.command == 'operator':
    +        # If timestamp is not given, robot uses current time on message receipt.
    +        robot.operator_comment(options.message)
    +        print("Added operator comment")
    +    elif options.command == 'event':
    +        add_event(robot)
    +    elif options.command == 'blob':
    +        add_blob(robot, options)
    +    elif options.command == 'protobuf':
    +        add_protobuf(robot, options)
    +    else:
    +        parser.print_help()
    +        return False
    +    return True
    +
    +
    +if __name__ == '__main__':
    +    if not main():
    +        sys.exit(1)
    diff --git a/python/examples/data_buffer/requirements.txt b/python/examples/data_buffer/requirements.txt
    new file mode 100644
    index 000000000..206b3c0da
    --- /dev/null
    +++ b/python/examples/data_buffer/requirements.txt
    @@ -0,0 +1,3 @@
    +-f ../../../prebuilt
    +
    +bosdyn-client >= 3.0
    diff --git a/python/examples/data_service/delete_pages.py b/python/examples/data_service/delete_pages.py
    index afbd5713b..140db0057 100644
    --- a/python/examples/data_service/delete_pages.py
    +++ b/python/examples/data_service/delete_pages.py
    @@ -47,8 +47,7 @@ def main(argv):
         """Command line interface."""
         parser = argparse.ArgumentParser()
         bosdyn.client.util.add_common_arguments(parser)
    -    parser.add_argument('-T', '--timespan', default='5m',
    -                        help='Time span (default last 5 minutes)')
    +    parser.add_argument('-T', '--timespan', default='5m', help='Time span (default last 5 minutes)')
         parser.add_argument('-R', '--robot-time', action='store_true',
                             help='Specified timespan is in robot time')
         parser.add_argument("--id", nargs="+", help="delete pages by page id")
    diff --git a/python/examples/data_service/get_comments.py b/python/examples/data_service/get_comments.py
    index 53120b795..5f5c8b8c1 100644
    --- a/python/examples/data_service/get_comments.py
    +++ b/python/examples/data_service/get_comments.py
    @@ -27,6 +27,7 @@ def get_comments(config):
         query.comments = True
         print(service_client.get_events_comments(query))
     
    +
     def main(argv):
         """Command line interface."""
         parser = argparse.ArgumentParser()
    diff --git a/python/examples/data_service/get_events.py b/python/examples/data_service/get_events.py
    index c503fbccb..e2fa4d16e 100644
    --- a/python/examples/data_service/get_events.py
    +++ b/python/examples/data_service/get_events.py
    @@ -24,9 +24,10 @@ def get_events(config):
         robot.authenticate(config.username, config.password)
         service_client = robot.ensure_client(DataServiceClient.default_service_name)
         query = data_index_protos.EventsCommentsSpec()
    -    query.events.add()
    +    query.events.add()  # pylint: disable=no-member
         print(service_client.get_events_comments(query))
     
    +
     def main(argv):
         """Command line interface."""
         parser = argparse.ArgumentParser()
    diff --git a/python/examples/data_service/get_index.py b/python/examples/data_service/get_index.py
    index 0d49686c9..58650a4b0 100644
    --- a/python/examples/data_service/get_index.py
    +++ b/python/examples/data_service/get_index.py
    @@ -41,6 +41,7 @@ def run_query(options, query):
         query.time_range.CopyFrom(timespec_to_robot_timespan(options.timespan, time_sync_endpoint))
         return service_client.get_data_index(query)
     
    +
     def get_blobs(options):
         """Get pages with message blobs from robot"""
         query = data_index_protos.DataQuery()
    @@ -52,24 +53,28 @@ def get_blobs(options):
             blobspec.message_type = options.message_type
         print(run_query(options, query))
     
    +
     def get_text(options):
         """Get pages with text-messages from robot"""
         query = data_index_protos.DataQuery()
         query.text_messages = True
         print(run_query(options, query))
     
    +
     def get_events(options):
         """Get pages with events from robot"""
         query = data_index_protos.DataQuery()
         query.events = True
         print(run_query(options, query))
     
    +
     def get_comments(options):
         """Get pages with operator comments from robot"""
         query = data_index_protos.DataQuery()
         query.comments = True
         print(run_query(options, query))
     
    +
     def main(argv):
         """Command line interface."""
         parser = argparse.ArgumentParser()
    diff --git a/python/examples/data_service/get_pages.py b/python/examples/data_service/get_pages.py
    index 449fb0e10..86d694c77 100644
    --- a/python/examples/data_service/get_pages.py
    +++ b/python/examples/data_service/get_pages.py
    @@ -36,8 +36,8 @@ def _show_page(page):
         is_open = " (open)" if page.is_open else ""
         print("{}\n    {} - {} ({})\n    {} ticks {} bytes  {} {}{}\n    {}\n".format(
             page.id, start_str, end_str, page.source, page.num_ticks, page.total_bytes,
    -        page.PageFormat.Name(page.format), page.Compression.Name(page.compression),
    -        is_open, page.path))
    +        page.PageFormat.Name(page.format), page.Compression.Name(page.compression), is_open,
    +        page.path))
     
     
     def get_pages(options):
    diff --git a/python/examples/data_service/requirements.txt b/python/examples/data_service/requirements.txt
    index 0fa91c82c..18f6d724d 100644
    --- a/python/examples/data_service/requirements.txt
    +++ b/python/examples/data_service/requirements.txt
    @@ -1,3 +1,3 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.1
    \ No newline at end of file
    +bosdyn-client >= 2.1
    \ No newline at end of file
    diff --git a/python/examples/directory/requirements.txt b/python/examples/directory/requirements.txt
    index b55ec2f63..27e4dca1f 100644
    --- a/python/examples/directory/requirements.txt
    +++ b/python/examples/directory/requirements.txt
    @@ -1,3 +1,3 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.0
    +bosdyn-client >= 2.0
    diff --git a/python/examples/disable_ir_emission/README.md b/python/examples/disable_ir_emission/README.md
    new file mode 100644
    index 000000000..f8d494c36
    --- /dev/null
    +++ b/python/examples/disable_ir_emission/README.md
    @@ -0,0 +1,27 @@
    +
    +
    +# Disable IR Emission
    +This example demonstrates how to use the IREnableDisableServiceClient to
    +enable/disable the robot's IR light emitters in the body and hand sensors.
    +
    +## Understanding Spot Programming
    +For your best learning experience, please use the [Quickstart Guide](../../../docs/python/quickstart.md)
    +found in the SDK's docs/python directory.  That will help you get your Python programming environment set up properly.  
    +
    +## Setup Dependencies
    +This example requires the bosdyn API and client to be installed, and must be run using python3. Using pip, these dependencies can be installed using:
    +
    +```
    +python3 -m pip install -r requirements.txt
    +```
    +## Run the Example
    +To run the example:
    +```
    +python3 disable_ir_emission.py --username USER --password PASSWORD ROBOT_IP (--enable | --disable)
    +```
    diff --git a/python/examples/disable_ir_emission/disable_ir_emission.py b/python/examples/disable_ir_emission/disable_ir_emission.py
    new file mode 100644
    index 000000000..a92174ada
    --- /dev/null
    +++ b/python/examples/disable_ir_emission/disable_ir_emission.py
    @@ -0,0 +1,46 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""Example for enabling/disabling the sensors that produce emissions near the IR spectrum."""
    +
    +import argparse
    +import sys
    +
    +import bosdyn.client
    +import bosdyn.client.util
    +from bosdyn.client.ir_enable_disable import IREnableDisableServiceClient
    +
    +from bosdyn.api import header_pb2
    +
    +
    +def main(argv):
    +    # Parse args
    +    parser = argparse.ArgumentParser()
    +    bosdyn.client.util.add_common_arguments(parser)
    +
    +    group = parser.add_mutually_exclusive_group(required=True)
    +    group.add_argument('--enable', action='store_true', dest='enable', help='Enable IR emissions')
    +    group.add_argument('--disable', action='store_false', dest='enable',
    +                       help='Disable IR emissions')
    +
    +    options = parser.parse_args(argv)
    +
    +    # Create robot object with an IREnableDisableServiceClient client.
    +    sdk = bosdyn.client.create_standard_sdk('ir_emission_test')
    +    robot = sdk.create_robot(options.hostname)
    +    robot.authenticate(options.username, options.password)
    +    ir_enable_disable_client = robot.ensure_client(
    +        IREnableDisableServiceClient.default_service_name)
    +
    +    # Send the request
    +    ir_enable_disable_client.set_ir_enabled(enable=options.enable)
    +
    +    return True
    +
    +
    +if __name__ == "__main__":
    +    if not main(sys.argv[1:]):
    +        sys.exit(1)
    diff --git a/python/examples/disable_ir_emission/requirements.txt b/python/examples/disable_ir_emission/requirements.txt
    new file mode 100644
    index 000000000..511bf4330
    --- /dev/null
    +++ b/python/examples/disable_ir_emission/requirements.txt
    @@ -0,0 +1,2 @@
    +-f ../../../prebuilt
    +bosdyn-client >= 3.0.0
    \ No newline at end of file
    diff --git a/python/examples/docking/requirements.txt b/python/examples/docking/requirements.txt
    index 86a056c48..8dce5b740 100644
    --- a/python/examples/docking/requirements.txt
    +++ b/python/examples/docking/requirements.txt
    @@ -1,3 +1,3 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.2
    +bosdyn-client >= 2.2
    diff --git a/python/examples/docs/arm_examples.md b/python/examples/docs/arm_examples.md
    index c71c8e09b..4dc9af708 100644
    --- a/python/examples/docs/arm_examples.md
    +++ b/python/examples/docs/arm_examples.md
    @@ -19,6 +19,7 @@ See the [robot services](../../../docs/concepts/robot_services.md) document and
     * [Stow/unstow Arm](../arm_stow_unstow/README.md)
     * [Arm and Mobility Command](../arm_and_mobility_command/README.md)
     * [Arm Command with Body Following](../arm_with_body_follow/README.md)
    +* [Arm Constrained Manipulation](../arm_constrained_manipulation/README.md)
     * [Arm Trajectory Motion Command](../arm_trajectory/README.md)
     * [Arm Joint Move Command](../arm_joint_move/README.md)
     * [Arm Force Control Command](../arm_force_control/README.md)
    diff --git a/python/examples/docs/autonomy_and_missions_examples.md b/python/examples/docs/autonomy_and_missions_examples.md
    index f559a062e..1b14cdd96 100644
    --- a/python/examples/docs/autonomy_and_missions_examples.md
    +++ b/python/examples/docs/autonomy_and_missions_examples.md
    @@ -12,11 +12,12 @@ The following examples demonstrate how to use the mission service and the autono
     
     ## Contents
     
    +* [Graph Nav Anchoring Optimization](../graph_nav_anchoring_optimization/README.md)
     * [Graph Nav Command Line](../graph_nav_command_line/README.md)
    +* [Graph Nav Extract Point Cloud](../graph_nav_extract_point_cloud/README.md)
     * [Graph Nav View Map](../graph_nav_view_map/README.md)
     * [Get Mission State](../get_mission_state/README.md)
     * [Remote Mission Service](../remote_mission_service/README.md)
     * [Mission Question Answerer](../mission_question_answerer/README.md)
     * [Mission Recorder](../mission_recorder/README.md)
    -* [Replay Mission](../replay_mission/README.md)
    -* [Machine Learning with the Network Compute Bridge](../network_compute_bridge/README.md)
    \ No newline at end of file
    +* [Replay Mission](../replay_mission/README.md)
    \ No newline at end of file
    diff --git a/python/examples/docs/basic_service_examples.md b/python/examples/docs/basic_service_examples.md
    index b039f4983..662cf6f6c 100644
    --- a/python/examples/docs/basic_service_examples.md
    +++ b/python/examples/docs/basic_service_examples.md
    @@ -21,4 +21,5 @@ The following examples show how to use various Boston Dynamics API services and
     * [Get Mission State](../get_mission_state/README.md)
     * [E-Stop](../estop/README.md)
     * [Time Sync](../time_sync/README.md)
    -* [Comms Test](../comms_test/README.md)
    \ No newline at end of file
    +* [Comms Test](../comms_test/README.md)
    +* [IR Enable/Disable](../disable_ir_emission/README.md)
    \ No newline at end of file
    diff --git a/python/examples/docs/data_acquisition_examples.md b/python/examples/docs/data_acquisition_examples.md
    index fa3273e35..19112eb8c 100644
    --- a/python/examples/docs/data_acquisition_examples.md
    +++ b/python/examples/docs/data_acquisition_examples.md
    @@ -17,5 +17,6 @@ The following examples show how to use the data acquisition service, and how to
     * [Ricoh Theta](../ricoh_theta/README.md)
     * [Web Cam Service](../web_cam_image_service/README.md)
     * [Test Image Service Implementation with Get Image](../get_image/README.md)
    +* [Project Depth Data on Visual Images](../get_depth_plus_visual_image/README.md)
     * [Cloud Upload](../cloud_upload/README.md)
    -* [Tester Programs](../tester_programs/README.md)
    \ No newline at end of file
    +* [Tester Programs](../tester_programs/README.md)
    diff --git a/python/examples/docs/logging_examples.md b/python/examples/docs/logging_examples.md
    index 8ce56f9be..614621e9c 100644
    --- a/python/examples/docs/logging_examples.md
    +++ b/python/examples/docs/logging_examples.md
    @@ -13,6 +13,7 @@ The following examples show how to use the different logging and data retrieval
     ## Contents
     
     * [BDDF Download](../bddf_download/README.md)
    +* [Data Buffer](../data_buffer/README.md)
     * [Data Service](../data_service/README.md)
     * [Logging](../logging/README.md)
     * [BDDF Download](../bddf_download/README.md)
    diff --git a/python/examples/docs/perception_world_objects_examples.md b/python/examples/docs/perception_world_objects_examples.md
    index 6aaa705fa..b9c09cc57 100644
    --- a/python/examples/docs/perception_world_objects_examples.md
    +++ b/python/examples/docs/perception_world_objects_examples.md
    @@ -25,3 +25,5 @@ The following examples show how to use Spot's perception system, through image s
     * [Fiducial Follow](../fiducial_follow/README.md)
     * [Spot Detect and Follow](../spot_detect_and_follow/README.md)
     * [Tensorflow Detector](../spot_tensorflow_detector/README.md)
    +* [Machine Learning with the Network Compute Bridge](../network_compute_bridge/README.md)
    +* [Fire Extinguisher Detector with the Network Compute Bridge](../network_compute_bridge/fire_extinguisher_server/README.md)
    \ No newline at end of file
    diff --git a/python/examples/docs/robot_behavior_examples.md b/python/examples/docs/robot_behavior_examples.md
    index 676ccd923..6aff2a517 100644
    --- a/python/examples/docs/robot_behavior_examples.md
    +++ b/python/examples/docs/robot_behavior_examples.md
    @@ -18,4 +18,7 @@ The following examples show how to command the robot to complete different behav
     * [Upload Choreographed Sequence](../upload_choreographed_sequence/README.md)
     * [Xbox Controller](../xbox_controller/README.md)
     * [WASD](../wasd/README.md)
    -* [Docking](../docking/README.md)
    \ No newline at end of file
    +* [Docking](../docking/README.md)
    +* [Post Docking Callbacks](../post_docking_callbacks/README.md)
    +* [Animation Recorder](../animation_recorder/README.md)
    +* [Auto Return](../auto_return/README.md)
    \ No newline at end of file
    diff --git a/python/examples/estop/requirements.txt b/python/examples/estop/requirements.txt
    index 46efa23cb..411ccda3e 100644
    --- a/python/examples/estop/requirements.txt
    +++ b/python/examples/estop/requirements.txt
    @@ -1,6 +1,6 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.0
    +bosdyn-client >= 2.0
     six
     pyqt5
     windows-curses==2.1.0; sys_platform == 'win32'
    diff --git a/python/examples/fiducial_follow/README.md b/python/examples/fiducial_follow/README.md
    index 4075b5a11..76889d40d 100644
    --- a/python/examples/fiducial_follow/README.md
    +++ b/python/examples/fiducial_follow/README.md
    @@ -38,18 +38,6 @@ On Linux, follow the instructions below to build the apriltag repository correct
     - The `make install` command of the external apriltag repo instructions will install the shared library and requires `LD_LIBRARY_PATH=/usr/local/lib` to be set.
     - For running the example in a virtualenv named `venv`, copy `apriltag.cpython-36m-x86_64-linux-gnu.so` from `~/.local/lib/python3.7/site-packages/` to `venv/lib/python3.7/site-packages/`. Note that "python3.7" is a placeholder for your system's python version.
     
    -On MacOS, follow the instructions below to build the apriltag repository correctly:
    -- Install cmake if it is not installed.
    -- Run `open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg` to install missing `stdio.h` file. The name of the `pkg` file might be different, depending on the MacOS version.
    -- Clone the apriltag git repository referenced above.
    -- Open the CMakeLists.txt and:
    -  - Remove `,-rpath=lib`
    -  - Replace libapriltag.so with libapriltag.dylib
    -- Run cmake and make commands as specified in the apriltag README file
    -- Copy the compiled apriltag library into your python site-packages folder. For example, if you configure a virtualenv named `venv`, execute this command `cp /Users/apitester/.local/lib/python3.7/site-packages/apriltag.cpython-37m-darwin.so venv/lib/python3.7/site-packages/`
    -
    -Note, in these instructions, the filepath `/Users/apitester/.local/lib/python3.7/site-packages/` represents the folder where the apriltag github library installs by default on MacOS, and it includes "apitester" as a placeholder for your system username, and "python3.7" as a placeholder for your system's python version.
    -
     This example is not supported on Windows OS if you want to use the external apriltag library. However, the example can be run using the world object service.
     
     
    @@ -62,6 +50,8 @@ The example depends on an external E-Stop endpoint application to configure E-St
     
     **Warning:** by default, obstacle avoidance is disabled so that the robot can walk to exactly to the location of the tag if necessary. This means the robot will get very close to walls or any people holding April Tags if the tag remains stationary while the program continues to iterate. Obstacle avoidance can be re-enabled, however this can prevent Spot from fully achieving the goal point (of the April Tag location).
     
    +Note: this example can only be run on Windows and Linux; Mac OS is not supported explicitly.
    +
     To run the example:
     ```
     python3 -m fiducial_follow --username USER --password PASSWORD ROBOT_IP
    diff --git a/python/examples/fiducial_follow/fiducial_follow.py b/python/examples/fiducial_follow/fiducial_follow.py
    index f927fa069..6cf300834 100644
    --- a/python/examples/fiducial_follow/fiducial_follow.py
    +++ b/python/examples/fiducial_follow/fiducial_follow.py
    @@ -38,6 +38,9 @@
     #pylint: disable=no-member
     LOGGER = logging.getLogger()
     
    +# Use this length to make sure we're commanding the head of the robot
    +# to a position instead of the center.
    +BODY_LENGTH = 1.1
     
     class FollowFiducial(object):
         """ Detect and follow a fiducial with Spot."""
    @@ -54,7 +57,7 @@ def __init__(self, robot, options):
             self._world_object_client = robot.ensure_client(WorldObjectClient.default_service_name)
     
             # Stopping Distance (x,y) offset from the tag and angle offset from desired angle.
    -        self._tag_offset = float(options.distance_margin)  #meters
    +        self._tag_offset = float(options.distance_margin) + BODY_LENGTH / 2.0  # meters
     
             # Maximum speeds.
             self._max_x_vel = 0.5
    @@ -67,7 +70,6 @@ def __init__(self, robot, options):
             self._use_world_object_service = (options.use_world_objects and
                                               self.check_if_version_has_world_objects(self._robot_id))
     
    -
             # Indicators for movement and image displays.
             self._standup = True  # Stand up the robot.
             self._movement_on = True  # Let the robot walk towards the fiducial.
    @@ -348,8 +350,8 @@ def pixel_coords_to_camera_coords(self, bbox, intrinsics, source_name):
                 # the same camera source.
                 self._camera_to_extrinsics_guess[source_name] = (True, (rvec, tvec))
     
    -            dist = math.sqrt(
    -                float(tvec[0][0])**2 + float(tvec[1][0])**2 + float(tvec[2][0])**2) / 1000.0
    +            dist = math.sqrt(float(tvec[0][0])**2 + float(tvec[1][0])**2 +
    +                             float(tvec[2][0])**2) / 1000.0
                 if dist < closest_dist:
                     closest_dist = dist
                     best_bbox = (tvec, rvec, source_name)
    @@ -437,9 +439,8 @@ def set_mobility_params(self):
                                                         obstacle_avoidance_padding=.001)
             body_control = self.set_default_body_control()
             if self._limit_speed:
    -            speed_limit = SE2VelocityLimit(
    -                max_vel=SE2Velocity(
    -                    linear=Vec2(x=self._max_x_vel, y=self._max_y_vel), angular=self._max_ang_vel))
    +            speed_limit = SE2VelocityLimit(max_vel=SE2Velocity(
    +                linear=Vec2(x=self._max_x_vel, y=self._max_y_vel), angular=self._max_ang_vel))
                 if not self._avoid_obstacles:
                     mobility_params = spot_command_pb2.MobilityParams(
                         obstacle_params=obstacles, vel_limit=speed_limit, body_control=body_control,
    @@ -483,8 +484,8 @@ def rotate_image(image, source_name):
         def make_camera_matrix(ints):
             """Transform the ImageResponse proto intrinsics into a camera matrix."""
             camera_matrix = np.array([[ints.focal_length.x, ints.skew.x, ints.principal_point.x],
    -                                  [ints.skew.y, ints.focal_length.y,
    -                                   ints.principal_point.y], [0, 0, 1]])
    +                                  [ints.skew.y, ints.focal_length.y, ints.principal_point.y],
    +                                  [0, 0, 1]])
             return camera_matrix
     
         def stop(self):
    @@ -591,8 +592,8 @@ def main():
                             help="Distance [meters] that the robot should stop from the fiducial.")
         parser.add_argument("--limit-speed", default=True, type=lambda x: (str(x).lower() == 'true'),
                             help="If the robot should limit its maximum speed.")
    -    parser.add_argument("--avoid-obstacles", default=False,
    -                        type=lambda x: (str(x).lower() == 'true'),
    +    parser.add_argument("--avoid-obstacles", default=False, type=lambda x:
    +                        (str(x).lower() == 'true'),
                             help="If the robot should have obstacle avoidance enabled.")
         parser.add_argument(
             "--use-world-objects", default=True, type=lambda x: (str(x).lower() == 'true'),
    diff --git a/python/examples/fiducial_follow/requirements.txt b/python/examples/fiducial_follow/requirements.txt
    index 8e224eefb..436348384 100644
    --- a/python/examples/fiducial_follow/requirements.txt
    +++ b/python/examples/fiducial_follow/requirements.txt
    @@ -1,6 +1,6 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.0
    +bosdyn-client >= 2.0
     Pillow == 6.1.0
     opencv-python >= 3.4.2.17
     numpy >= 1.16.4
    diff --git a/python/examples/frame_trajectory_command/README.md b/python/examples/frame_trajectory_command/README.md
    index 6cf80a2ec..310319662 100644
    --- a/python/examples/frame_trajectory_command/README.md
    +++ b/python/examples/frame_trajectory_command/README.md
    @@ -8,7 +8,8 @@ Development Kit License (20191101-BDSDK-SL).
     
     # Frame Trajectory Commands
     
    -Spot's knowledge of different frames can be used to make issuing robot commands much simpler. This example shows how to query the robot for its current position in the visual and odom frames. It then shows how to issue a trajectory command in the visual or odom frames that describes the relative motion of the body. For example, it first issues a command telling Spot to walk forward one meter, but issues the trajectory described relative to the visual frame.
    +Spot's knowledge of different frames can be used to make issuing robot commands much simpler. This example shows how to query the robot for its current position in the visual and odom frames. It then shows how to issue a trajectory command in the visual or odom frames that describes the relative motion of the body.
    +Command line arguments are used to specify an offset to the current body's position, which is then commanded in odom or vision frame.
     
     Note that trajectory commands cannot be issued in the body frame since this creates a goal point that is continually moving, so the trajectory can never reach the goal.
     
    @@ -20,7 +21,7 @@ See [the geometry and frames documentation](../../../docs/concepts/geometry_and_
     You will have to launch a software E-Stop separately in order to run this example. See [the E-Stop examples.](../estop/README.md)
     
     ## Run the Example
    -To run the example:
    +To run the example (moving forward 1 meter):
     ```
    -python3 frame_trajectory_command.py --username USER --password PASSWORD ROBOT_IP
    +python3 frame_trajectory_command.py --username USER --password PASSWORD ROBOT_IP --dx 1
     ```
    diff --git a/python/examples/frame_trajectory_command/frame_trajectory_command.py b/python/examples/frame_trajectory_command/frame_trajectory_command.py
    index 62769e05b..ff48596bc 100644
    --- a/python/examples/frame_trajectory_command/frame_trajectory_command.py
    +++ b/python/examples/frame_trajectory_command/frame_trajectory_command.py
    @@ -4,25 +4,34 @@
     # is subject to the terms and conditions of the Boston Dynamics Software
     # Development Kit License (20191101-BDSDK-SL).
     
    -"""Simple robot command capture tutorial."""
    +"""Command the robot to go to an offset position using a trajectory command."""
     
    -import argparse
     import math
     import sys
     import time
    +from bosdyn.api.basic_command_pb2 import RobotCommandFeedbackStatus
     import bosdyn.client
     import bosdyn.client.util
     from bosdyn.client.robot_state import RobotStateClient
     from bosdyn.client.robot_command import RobotCommandClient, RobotCommandBuilder, blocking_stand
    -from bosdyn.api import geometry_pb2 as geo
     from bosdyn.client import math_helpers
    -from bosdyn.client.frame_helpers import ODOM_FRAME_NAME, VISION_FRAME_NAME, get_odom_tform_body, get_vision_tform_body
    +from bosdyn.client.frame_helpers import ODOM_FRAME_NAME, VISION_FRAME_NAME, BODY_FRAME_NAME, get_se2_a_tform_b
     from bosdyn.client.lease import LeaseClient, LeaseKeepAlive
     
     
     def main():
    +    import argparse
         parser = argparse.ArgumentParser()
         bosdyn.client.util.add_common_arguments(parser)
    +    parser.add_argument('--dx', default=0, type=float,
    +                        help='Position offset in body frame (meters forward).')
    +    parser.add_argument('--dy', default=0, type=float,
    +                        help='Position offset in body frame (meters left).')
    +    parser.add_argument('--dyaw', default=0, type=float,
    +                        help='Position offset in body frame (degrees ccw).')
    +    parser.add_argument('--frame', choices=[VISION_FRAME_NAME, ODOM_FRAME_NAME],
    +                        default=ODOM_FRAME_NAME, help='Send the command in this frame.')
    +    parser.add_argument('--stairs', action='store_true', help='Move the robot in stairs mode.')
         options = parser.parse_args()
     
         # Create robot object.
    @@ -36,69 +45,58 @@ def main():
     
         # Create the lease client.
         lease_client = robot.ensure_client(LeaseClient.default_service_name)
    -    lease = lease_client.acquire()
    -    robot.time_sync.wait_for_sync()
    -    lk = bosdyn.client.lease.LeaseKeepAlive(lease_client)
     
         # Setup clients for the robot state and robot command services.
         robot_state_client = robot.ensure_client(RobotStateClient.default_service_name)
         robot_command_client = robot.ensure_client(RobotCommandClient.default_service_name)
     
    -    # Power on the robot and stand it up.
    -    robot.power_on()
    -    blocking_stand(robot_command_client)
    -
    -    # Get robot state information. Specifically, we are getting the vision_tform_body transform to understand
    -    # the robot's current position in the vision frame.
    -    vision_tform_body = get_vision_tform_body(
    -        robot_state_client.get_robot_state().kinematic_state.transforms_snapshot)
    -
    -    # We want to command a trajectory to go forward one meter in the x-direction of the body.
    -    # It is simple to define this trajectory relative to the body frame, since we know that will be
    -    # just 1 meter forward in the x-axis of the body.
    -    # Note that the rotation is just math_helpers.Quat(), which is the identity quaternion. We want the
    -    # rotation of the body at the goal to match the rotation of the body currently, so we do not need
    -    # to transform the rotation.
    -    body_tform_goal = math_helpers.SE3Pose(x=1, y=0, z=0, rot=math_helpers.Quat())
    -    # We can then transform this transform to get the goal position relative to the vision frame.
    -    vision_tform_goal = vision_tform_body * body_tform_goal
    -
    -    # Command the robot to go to the goal point in the vision frame. The command will stop at the new
    -    # position in the vision frame.
    -    robot_cmd = RobotCommandBuilder.synchro_se2_trajectory_point_command(goal_x=vision_tform_goal.x,
    -                                                     goal_y=vision_tform_goal.y,
    -                                                     goal_heading=vision_tform_goal.rot.to_yaw(),
    -                                                     frame_name=VISION_FRAME_NAME)
    -    end_time = 2.0
    -    robot_command_client.robot_command(lease=None, command=robot_cmd,
    -                                       end_time_secs=time.time() + end_time)
    -    time.sleep(end_time)
    -
    -    # Get new robot state information after moving the robot. Here we are getting the transform odom_tform_body,
    -    # which describes the robot body's position in the odom frame.
    -    odom_tform_body = get_odom_tform_body(
    -        robot_state_client.get_robot_state().kinematic_state.transforms_snapshot)
    -
    -    # We want to command a trajectory to go backwards one meter and to the left one meter.
    -    # It is simple to define this trajectory relative to the body frame, since we know that will be
    -    # just 1 meter backwards (negative-value) in the x-axis of the body and one meter left (positive-value)
    -    # in the y-axis of the body.
    -    body_tform_goal = math_helpers.SE3Pose(x=-1, y=1, z=0, rot=math_helpers.Quat())
    -    # We can then transform this transform to get the goal position relative to the odom frame.
    -    odom_tform_goal = odom_tform_body * body_tform_goal
    -
    -    # Command the robot to go to the goal point in the odom frame. The command will stop at the new
    -    # position in the odom frame.
    -    robot_cmd = RobotCommandBuilder.synchro_se2_trajectory_point_command(goal_x=odom_tform_goal.x,
    -                                                     goal_y=odom_tform_goal.y,
    -                                                     goal_heading=odom_tform_goal.rot.to_yaw(),
    -                                                     frame_name=ODOM_FRAME_NAME)
    -    end_time = 5.0
    -    robot_command_client.robot_command(lease=None, command=robot_cmd,
    -                                       end_time_secs=time.time() + end_time)
    -    time.sleep(end_time)
    -
    -    return True
    +    lease_client.acquire()
    +    with LeaseKeepAlive(lease_client, return_at_exit=True):
    +        # Power on the robot and stand it up.
    +        robot.time_sync.wait_for_sync()
    +        robot.power_on()
    +        blocking_stand(robot_command_client)
    +
    +        try:
    +            return relative_move(options.dx, options.dy, math.radians(options.dyaw), options.frame,
    +                                 robot_command_client, robot_state_client, stairs=options.stairs)
    +        finally:
    +            # Send a Stop at the end, regardless of what happened.
    +            robot_command_client.robot_command(RobotCommandBuilder.stop_command())
    +
    +
    +def relative_move(dx, dy, dyaw, frame_name, robot_command_client, robot_state_client, stairs=False):
    +    transforms = robot_state_client.get_robot_state().kinematic_state.transforms_snapshot
    +
    +    # Build the transform for where we want the robot to be relative to where the body currently is.
    +    body_tform_goal = math_helpers.SE2Pose(x=dx, y=dy, angle=dyaw)
    +    # We do not want to command this goal in body frame because the body will move, thus shifting
    +    # our goal. Instead, we transform this offset to get the goal position in the output frame
    +    # (which will be either odom or vision).
    +    out_tform_body = get_se2_a_tform_b(transforms, frame_name, BODY_FRAME_NAME)
    +    out_tform_goal = out_tform_body * body_tform_goal
    +
    +    # Command the robot to go to the goal point in the specified frame. The command will stop at the
    +    # new position.
    +    robot_cmd = RobotCommandBuilder.synchro_se2_trajectory_point_command(
    +        goal_x=out_tform_goal.x, goal_y=out_tform_goal.y, goal_heading=out_tform_goal.angle,
    +        frame_name=frame_name, params=RobotCommandBuilder.mobility_params(stair_hint=stairs))
    +    end_time = 10.0
    +    cmd_id = robot_command_client.robot_command(lease=None, command=robot_cmd,
    +                                                end_time_secs=time.time() + end_time)
    +    # Wait until the robot has reached the goal.
    +    while True:
    +        feedback = robot_command_client.robot_command_feedback(cmd_id)
    +        mobility_feedback = feedback.feedback.synchronized_feedback.mobility_command_feedback
    +        if mobility_feedback.status != RobotCommandFeedbackStatus.STATUS_PROCESSING:
    +            print("Failed to reach the goal")
    +            return False
    +        traj_feedback = mobility_feedback.se2_trajectory_feedback
    +        if (traj_feedback.status == traj_feedback.STATUS_AT_GOAL and
    +                traj_feedback.body_movement_status == traj_feedback.BODY_STATUS_SETTLED):
    +            print("Arrived at the goal.")
    +            return True
    +        time.sleep(1)
     
     
     if __name__ == "__main__":
    diff --git a/python/examples/frame_trajectory_command/requirements.txt b/python/examples/frame_trajectory_command/requirements.txt
    index ccf6f8ef8..e2e457ae8 100644
    --- a/python/examples/frame_trajectory_command/requirements.txt
    +++ b/python/examples/frame_trajectory_command/requirements.txt
    @@ -1,3 +1,3 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.1
    +bosdyn-client >= 2.1
    diff --git a/python/examples/get_depth_plus_visual_image/README.md b/python/examples/get_depth_plus_visual_image/README.md
    new file mode 100644
    index 000000000..b0c2ac1ab
    --- /dev/null
    +++ b/python/examples/get_depth_plus_visual_image/README.md
    @@ -0,0 +1,28 @@
    +
    +
    +# API Example - Visualize Depth in Visual Image
    +
    +This example demonstrates how to use the `depth_in_visual_frame` image sources to visualize depth in a visual image.
    +
    +## Setup Dependencies
    +This example requires the bosdyn API and client to be installed, and must be run using python3. Using pip, these dependencies can be installed using:
    +
    +```
    +python3 -m pip install -r requirements.txt
    +```
    +
    +## Running the Example
    +To run the example:
    +```
    +python3 get_depth_plus_visual_image.py --username USER --password PASSWORD ROBOT_IP
    +```
    +
    +## Example output
    +
    +![Example image](documentation/example_image.jpg)
    diff --git a/python/examples/get_depth_plus_visual_image/documentation/example_image.jpg b/python/examples/get_depth_plus_visual_image/documentation/example_image.jpg
    new file mode 100644
    index 000000000..e7d22551b
    Binary files /dev/null and b/python/examples/get_depth_plus_visual_image/documentation/example_image.jpg differ
    diff --git a/python/examples/get_depth_plus_visual_image/get_depth_plus_visual_image.py b/python/examples/get_depth_plus_visual_image/get_depth_plus_visual_image.py
    new file mode 100644
    index 000000000..827099ac9
    --- /dev/null
    +++ b/python/examples/get_depth_plus_visual_image/get_depth_plus_visual_image.py
    @@ -0,0 +1,96 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""Example demonstrating capture of both visual and depth images and then overlaying them."""
    +
    +import argparse
    +import sys
    +
    +import bosdyn.client
    +import bosdyn.client.util
    +from bosdyn.client.image import ImageClient
    +
    +import cv2
    +import numpy as np
    +
    +
    +def main(argv):
    +    # Parse args
    +    parser = argparse.ArgumentParser()
    +    bosdyn.client.util.add_common_arguments(parser)
    +    parser.add_argument('--to-depth', help='Convert to the depth frame. Default is convert to visual.',
    +                        action='store_true')
    +    parser.add_argument('--camera', help='Camera to acquire image from.', default='frontleft',\
    +                        choices=['frontleft', 'frontright', 'left', 'right', 'back',
    +                        ])
    +    parser.add_argument('--auto-rotate', help='rotate right and front images to be upright',
    +                        action='store_true')
    +    options = parser.parse_args(argv)
    +
    +    if options.to_depth:
    +        sources = [ options.camera + '_depth', options.camera + '_visual_in_depth_frame' ]
    +    else:
    +        sources = [ options.camera + '_depth_in_visual_frame', options.camera + '_fisheye_image' ]
    +
    +
    +    # Create robot object with an image client.
    +    sdk = bosdyn.client.create_standard_sdk('image_depth_plus_visual')
    +    robot = sdk.create_robot(options.hostname)
    +    robot.authenticate(options.username, options.password)
    +    image_client = robot.ensure_client(ImageClient.default_service_name)
    +
    +    # Capture and save images to disk
    +    image_responses = image_client.get_image_from_sources(sources)
    +
    +    # Image responses are in the same order as the requests.
    +    # Convert to opencv images.
    +
    +    if len(image_responses) < 2:
    +        print('Error: failed to get images.')
    +        return False
    +
    +    # Depth is a raw bytestream
    +    cv_depth = np.frombuffer(image_responses[0].shot.image.data, dtype=np.uint16)
    +    cv_depth = cv_depth.reshape(image_responses[0].shot.image.rows,
    +                                image_responses[0].shot.image.cols)
    +
    +    # Visual is a JPEG
    +    cv_visual = cv2.imdecode(np.frombuffer(image_responses[1].shot.image.data, dtype=np.uint8), -1)
    +
    +    # Convert the visual image from a single channel to RGB so we can add color
    +    visual_rgb = cv2.cvtColor(cv2.cvtColor(cv_visual, cv2.COLOR_RGB2GRAY), cv2.COLOR_GRAY2RGB) if len(cv_visual.shape) == 3 else cv2.cvtColor(cv_visual, cv2.COLOR_GRAY2RGB)
    +
    +
    +    # Map depth ranges to color
    +
    +    # cv2.applyColorMap() only supports 8-bit; convert from 16-bit to 8-bit and do scaling
    +    min_val = np.min(cv_depth)
    +    max_val = np.max(cv_depth)
    +    depth_range = max_val - min_val
    +    depth8 = (255.0 / depth_range * (cv_depth - min_val)).astype('uint8')
    +    depth8_rgb = cv2.cvtColor(depth8, cv2.COLOR_GRAY2RGB)
    +    depth_color = cv2.applyColorMap(depth8_rgb, cv2.COLORMAP_JET)
    +
    +    # Add the two images together.
    +    out = cv2.addWeighted(visual_rgb, 0.5, depth_color, 0.5, 0)
    +
    +    if options.auto_rotate:
    +        if image_responses[0].source.name[0:5] == "front":
    +            out = cv2.rotate(out, cv2.ROTATE_90_CLOCKWISE)
    +
    +        elif image_responses[0].source.name[0:5] == "right":
    +            out = cv2.rotate(out, cv2.ROTATE_180)
    +
    +    # Write the image out.
    +    filename = options.camera + ".jpg"
    +    cv2.imwrite(filename, out)
    +
    +    return True
    +
    +
    +if __name__ == "__main__":
    +    if not main(sys.argv[1:]):
    +        sys.exit(1)
    diff --git a/python/examples/network_compute_bridge/requirements_client_only.txt b/python/examples/get_depth_plus_visual_image/requirements.txt
    similarity index 71%
    rename from python/examples/network_compute_bridge/requirements_client_only.txt
    rename to python/examples/get_depth_plus_visual_image/requirements.txt
    index 029684b2a..28ea6f17e 100644
    --- a/python/examples/network_compute_bridge/requirements_client_only.txt
    +++ b/python/examples/get_depth_plus_visual_image/requirements.txt
    @@ -1,5 +1,5 @@
     -f ../../../prebuilt
    -bosdyn-client ~= 2.3
    +bosdyn-client >= 2.0.dev
     
     opencv-python >= 3.4.2.17
     numpy >= 1.16.4
    diff --git a/python/examples/get_image/README.md b/python/examples/get_image/README.md
    index a13a41ec0..a41ac40a8 100644
    --- a/python/examples/get_image/README.md
    +++ b/python/examples/get_image/README.md
    @@ -8,16 +8,18 @@ Development Kit License (20191101-BDSDK-SL).
     
     # Using the Image Service
     
    -This example program demonstrates how to list the different image sources available to query. Additionally, this example program shows how to capture an image from one or more different image sources, decode the response data, and save each image locally in a file named after the image source.
    +This example program demonstrates how to list the different image sources available to query. Additionally, this example program shows how to capture an image from one or more different image sources, decode the response data, and either save each image locally in a file named after the image source or display it in a live preview.
     
     ## Setup Dependencies
    +
     This example requires the bosdyn API and client to be installed, and must be run using python3. Using pip, these dependencies can be installed using:
     
     ```
     python3 -m pip install -r requirements.txt
     ```
     
    -## Running the Example
    +## Running the Get-Image Example
    +
     The example can be used to list the available image sources, as well as query and save image data from each image source. By default, the example is configured to communicate with the robot cameras, however it can be updated to
     
     To run the example and query images from the base robot cameras:
    @@ -31,3 +33,21 @@ Instead of providing the argument `--image-sources`, the command line argument `
     To test an image service other than the base robot cameras, the `--image-service` argument can be passed with the image service name being tested. For example, to test the [web cam service](../web_cam_image_service/README.md), the argument `--image-service web-cam-service` can be included when running the example. Since the other image services will be registered with the robot's directory service, the get_image example can be run from any computer and just needs an API connection to the robot to be able to access the external image service and its images.
     
     Note that the front left and front right cameras on Spot are rotated 90 degrees counterclockwise from upright, and the right camera on Spot is rotated 180 degrees from upright. As a result, the corresponding images aren't initially saved with the same orientation as is seen on the tablet. By adding the command line argument `--auto-rotate`, this example code automatically rotates all images from Spot to be saved in the orientation they are seen on the tablet screen.
    +
    +## Running the Image-Viewer Example
    +
    +This example can be used to create popup windows which show a live preview of the image sources specified.
    +
    +To run the example and display images from the base robot cameras:
    +```
    +python3 image_viewer.py --username USER --password PASSWORD ROBOT_IP --image-sources frontleft_fisheye_image
    +```
    +
    +The command specifies each source from which images should be captured using the command line argument `--image-sources`, exactly like the Get-Image example. As well, the arguments `--image-service` and `--auto-rotate` are used identically as described in the section above for the Get-Image example.
    +
    +The argument `--jpeg-quality-percent` can be provided to change the JPEG quality of the requested images; this argument describes a percentage (0-100) for the quality.
    +
    +The argument `--capture-delay` can be used to change the wait time between image captures in milliseconds.
    +
    +If only a single image source is requested to be displayed, by default the program will make the image viewer show a full screen stream. To disable this, provide the argument `--disable-full-screen`, which will make the image stream display auto-size to approximately the size of the image.
    +
    diff --git a/python/examples/get_image/get_image.py b/python/examples/get_image/get_image.py
    index 6d3b97ec0..f3bd2c20b 100644
    --- a/python/examples/get_image/get_image.py
    +++ b/python/examples/get_image/get_image.py
    @@ -17,13 +17,23 @@
     import cv2
     import numpy as np
     
    +from scipy import ndimage
    +
    +ROTATION_ANGLE = {
    +    'back_fisheye_image': 0,
    +    'frontleft_fisheye_image': -78,
    +    'frontright_fisheye_image': -102,
    +    'left_fisheye_image': 0,
    +    'right_fisheye_image': 180
    +}
     
     def main(argv):
         # Parse args
         parser = argparse.ArgumentParser()
         bosdyn.client.util.add_common_arguments(parser)
         parser.add_argument('--list', help='list image sources', action='store_true')
    -    parser.add_argument('--auto-rotate', help='rotate right and front images to be upright', action='store_true')
    +    parser.add_argument('--auto-rotate', help='rotate right and front images to be upright',
    +                        action='store_true')
         parser.add_argument('--image-sources', help='Get image from source(s)', action='append')
         parser.add_argument('--image-service', help='Name of the image service to query.',
                             default=ImageClient.default_service_name)
    @@ -83,12 +93,8 @@ def main(argv):
                     img = cv2.imdecode(img, -1)
     
                 if options.auto_rotate:
    -                if image.source.name[0:5] == "front":
    -                    img = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
    +                img = ndimage.rotate(img, ROTATION_ANGLE[image.source.name])
     
    -                elif image.source.name[0:5] == "right":
    -                    img = cv2.rotate(img, cv2.ROTATE_180)
    -            
     
                 # Save the image from the GetImage request to the current directory with the filename
                 # matching that of the image source.
    diff --git a/python/examples/get_image/image_viewer.py b/python/examples/get_image/image_viewer.py
    new file mode 100644
    index 000000000..02e9faeb4
    --- /dev/null
    +++ b/python/examples/get_image/image_viewer.py
    @@ -0,0 +1,145 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""Simple image display example."""
    +
    +import argparse
    +import sys
    +
    +from bosdyn.api import image_pb2
    +import bosdyn.client
    +from bosdyn.client.time_sync import TimedOutError
    +import bosdyn.client.util
    +from bosdyn.client.image import ImageClient, build_image_request
    +from bosdyn.api import image_pb2
    +import cv2
    +import numpy as np
    +from scipy import ndimage
    +
    +import logging
    +_LOGGER = logging.getLogger(__name__)
    +
    +VALUE_FOR_Q_KEYSTROKE = 113
    +VALUE_FOR_ESC_KEYSTROKE = 27
    +
    +ROTATION_ANGLE = {
    +    'back_fisheye_image': 0,
    +    'frontleft_fisheye_image': -78,
    +    'frontright_fisheye_image': -102,
    +    'left_fisheye_image': 0,
    +    'right_fisheye_image': 180
    +}
    +
    +def image_to_opencv(image, auto_rotate=True):
    +    """Convert an image proto message to an openCV image."""
    +    num_channels = 1  # Assume a default of 1 byte encodings.
    +    if image.shot.image.pixel_format == image_pb2.Image.PIXEL_FORMAT_DEPTH_U16:
    +        dtype = np.uint16
    +        extension = ".png"
    +    else:
    +        dtype = np.uint8
    +        if image.shot.image.pixel_format == image_pb2.Image.PIXEL_FORMAT_RGB_U8:
    +            num_channels = 3
    +        elif image.shot.image.pixel_format == image_pb2.Image.PIXEL_FORMAT_RGBA_U8:
    +            num_channels = 4
    +        elif image.shot.image.pixel_format == image_pb2.Image.PIXEL_FORMAT_GREYSCALE_U8:
    +            num_channels = 1
    +        elif image.shot.image.pixel_format == image_pb2.Image.PIXEL_FORMAT_GREYSCALE_U16:
    +            num_channels = 1
    +            dtype = np.uint16
    +        extension = ".jpg"
    +
    +    img = np.frombuffer(image.shot.image.data, dtype=dtype)
    +    if image.shot.image.format == image_pb2.Image.FORMAT_RAW:
    +        try:
    +            # Attempt to reshape array into a RGB rows X cols shape.
    +            img = img.reshape((image.shot.image.rows, image.shot.image.cols, num_channels))
    +        except ValueError:
    +            # Unable to reshape the image data, trying a regular decode.
    +            img = cv2.imdecode(img, -1)
    +    else:
    +        img = cv2.imdecode(img, -1)
    +
    +    if auto_rotate:
    +        img = ndimage.rotate(img, ROTATION_ANGLE[image.source.name])
    +
    +    return img, extension
    +
    +
    +def reset_image_client(robot):
    +    """Recreate the ImageClient from the robot object."""
    +    del robot.service_clients_by_name['image']
    +    del robot.channels_by_authority['api.spot.robot']
    +    return robot.ensure_client('image')
    +
    +
    +def main(argv):
    +    # Parse args
    +    parser = argparse.ArgumentParser()
    +    bosdyn.client.util.add_common_arguments(parser)
    +    parser.add_argument('--image-sources', help='Get image from source(s)', action='append')
    +    parser.add_argument('--image-service', help='Name of the image service to query.',
    +                        default=ImageClient.default_service_name)
    +    parser.add_argument('-j', '--jpeg-quality-percent', help="JPEG quality percentage (0-100)",
    +                        type=int, default=50)
    +    parser.add_argument('-c', '--capture-delay', help="Time [ms] to wait before the next capture",
    +                        type=int, default=100)
    +    parser.add_argument('--disable-full-screen', help="A single image source gets displayed full screen by default. This flag disables that.", action='store_true')
    +    parser.add_argument('--auto-rotate', help='rotate right and front images to be upright',
    +                        action='store_true')
    +    options = parser.parse_args(argv)
    +
    +    # Create robot object with an image client.
    +    sdk = bosdyn.client.create_standard_sdk('image_capture')
    +    robot = sdk.create_robot(options.hostname)
    +    robot.authenticate(options.username, options.password)
    +    robot.sync_with_directory()
    +    robot.time_sync.wait_for_sync()
    +
    +    image_client = robot.ensure_client(options.image_service)
    +    requests = [
    +        build_image_request(source, quality_percent=options.jpeg_quality_percent)
    +        for source in options.image_sources
    +    ]
    +
    +    for image_source in options.image_sources:
    +        cv2.namedWindow(image_source, cv2.WINDOW_NORMAL)
    +        if len(options.image_sources) > 1 or options.disable_full_screen:
    +            cv2.setWindowProperty(image_source, cv2.WND_PROP_AUTOSIZE, cv2.WINDOW_AUTOSIZE)
    +        else:
    +            cv2.setWindowProperty(image_source, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
    +
    +    keystroke = None
    +    timeout_count_before_reset = 0
    +    while keystroke != VALUE_FOR_Q_KEYSTROKE and keystroke != VALUE_FOR_ESC_KEYSTROKE:
    +        try:
    +            images_future = image_client.get_image_async(requests, timeout=0.5)
    +            while not images_future.done():
    +                keystroke = cv2.waitKey(25)
    +                print(keystroke)
    +                if keystroke == VALUE_FOR_ESC_KEYSTROKE or keystroke == VALUE_FOR_Q_KEYSTROKE:
    +                    sys.exit(1)
    +            images = images_future.result()
    +        except TimedOutError as time_err:
    +            if timeout_count_before_reset == 5:
    +                # To attempt to handle bad comms and continue the live image stream, try recreating the
    +                # image client after having an RPC timeout 5 times.
    +                _LOGGER.info("Resetting image client after 5+ timeout errors.")
    +                image_client = reset_image_client(robot)
    +                timeout_count_before_reset = 0
    +            else:
    +                timeout_count_before_reset += 1
    +        except Exception as err:
    +            _LOGGER.warning(err)
    +            continue
    +        for i in range(len(images)):
    +            image, _ = image_to_opencv(images[i], options.auto_rotate)
    +            cv2.imshow(images[i].source.name, image)
    +        keystroke = cv2.waitKey(options.capture_delay)
    +
    +if __name__ == "__main__":
    +    if not main(sys.argv[1:]):
    +        sys.exit(1)
    diff --git a/python/examples/get_image/requirements.txt b/python/examples/get_image/requirements.txt
    index 1f7358ccf..7eaed4979 100644
    --- a/python/examples/get_image/requirements.txt
    +++ b/python/examples/get_image/requirements.txt
    @@ -1,6 +1,7 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.0
    +bosdyn-client >= 2.0
     
     opencv-python >= 3.4.2.17
     numpy >= 1.16.4
    +scipy>=1.3.1
    diff --git a/python/examples/get_mission_state/requirements.txt b/python/examples/get_mission_state/requirements.txt
    index bc5f8a200..8da408cb1 100644
    --- a/python/examples/get_mission_state/requirements.txt
    +++ b/python/examples/get_mission_state/requirements.txt
    @@ -1,3 +1,3 @@
     -f ../../../prebuilt
    -bosdyn-client ~= 2.0
    -bosdyn-mission ~= 2.0
    +bosdyn-client >= 2.0
    +bosdyn-mission >= 2.0
    diff --git a/python/examples/get_robot_state/get_robot_state.py b/python/examples/get_robot_state/get_robot_state.py
    index 43203be47..759b3774a 100644
    --- a/python/examples/get_robot_state/get_robot_state.py
    +++ b/python/examples/get_robot_state/get_robot_state.py
    @@ -16,15 +16,11 @@
     def main():
         import argparse
     
    -    commands = {
    -        'state': "get_robot_state",
    -        'hardware': "get_hardware_config_with_link_info",
    -        'metrics': "get_robot_metrics",
    -    }
    +    commands = set(['state', 'hardware', 'metrics'])
     
         parser = argparse.ArgumentParser()
         bosdyn.client.util.add_common_arguments(parser)
    -    parser.add_argument('command', choices=list(commands.keys()), help='Command to run')
    +    parser.add_argument('command', choices=list(commands), help='Command to run')
         options = parser.parse_args()
     
         # Create robot object with an image client.
    @@ -34,9 +30,12 @@ def main():
         robot_state_client = robot.ensure_client(RobotStateClient.default_service_name)
     
         # Make a robot state request
    -    request_fn = getattr(robot_state_client, commands[options.command])
    -    response = request_fn()
    -    print(response)
    +    if options.command == 'state':
    +        print(robot_state_client.get_robot_state())
    +    elif options.command == 'hardware':
    +        print(robot_state_client.get_hardware_config_with_link_info())
    +    elif options.command == 'metrics':
    +        print(robot_state_client.get_robot_metrics())
     
         return True
     
    diff --git a/python/examples/get_robot_state/requirements.txt b/python/examples/get_robot_state/requirements.txt
    index 816def74b..dcb5461e1 100644
    --- a/python/examples/get_robot_state/requirements.txt
    +++ b/python/examples/get_robot_state/requirements.txt
    @@ -1,2 +1,2 @@
     -f ../../../prebuilt
    -bosdyn-client ~= 2.0
    +bosdyn-client >= 2.0
    diff --git a/python/examples/get_robot_state_async/requirements.txt b/python/examples/get_robot_state_async/requirements.txt
    index 816def74b..dcb5461e1 100644
    --- a/python/examples/get_robot_state_async/requirements.txt
    +++ b/python/examples/get_robot_state_async/requirements.txt
    @@ -1,2 +1,2 @@
     -f ../../../prebuilt
    -bosdyn-client ~= 2.0
    +bosdyn-client >= 2.0
    diff --git a/python/examples/get_world_objects/get_world_objects.py b/python/examples/get_world_objects/get_world_objects.py
    index 74ad9fd0e..1426d8176 100644
    --- a/python/examples/get_world_objects/get_world_objects.py
    +++ b/python/examples/get_world_objects/get_world_objects.py
    @@ -38,10 +38,11 @@ def main(argv):
         print("World objects: " + str(world_objects))
         # Examine the transform snapshot for the world object!
         for world_obj in world_objects:
    -        print("ID: "+str(world_obj.id))
    +        print("ID: " + str(world_obj.id))
             full_snapshot = world_obj.transforms_snapshot
             for edge in full_snapshot.child_to_parent_edge_map:
    -            print("Child frame name: " + edge + ". Parent frame name: " + full_snapshot.child_to_parent_edge_map[edge].parent_frame_name)
    +            print("Child frame name: " + edge + ". Parent frame name: " +
    +                  full_snapshot.child_to_parent_edge_map[edge].parent_frame_name)
     
         # Get all fiducial objects (an object of a specific type).
         request_fiducials = [world_object_pb2.WORLD_OBJECT_APRILTAG]
    diff --git a/python/examples/get_world_objects/requirements.txt b/python/examples/get_world_objects/requirements.txt
    index b55ec2f63..27e4dca1f 100644
    --- a/python/examples/get_world_objects/requirements.txt
    +++ b/python/examples/get_world_objects/requirements.txt
    @@ -1,3 +1,3 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.0
    +bosdyn-client >= 2.0
    diff --git a/python/examples/graph_nav_anchoring_optimization/README.md b/python/examples/graph_nav_anchoring_optimization/README.md
    new file mode 100644
    index 000000000..d3389691e
    --- /dev/null
    +++ b/python/examples/graph_nav_anchoring_optimization/README.md
    @@ -0,0 +1,222 @@
    +
    +
    +
    +# Graph Nav Anchoring Optimization Example
    +
    +This example demonstrates how to use the map processing service to align a graph nav map to a blueprint. This assumes that you have a running robot connected to the client.
    +
    +# Setup Dependencies
    +
    +These examples require the bosdyn API and client to be installed, and must be run using python3. Using pip, these dependencies can be installed using:
    +
    +```
    +python3 -m pip install -r requirements.txt
    +```
    +
    +The example also requires matplotlib.  Depending on your system you may need to set up a [backend](https://matplotlib.org/stable/tutorials/introductory/usage.html#what-is-a-backend) for it to display properly.  One possible backend to use is Qt5: `python3 -m pip install pyqt5` and set the environment variable `MPLBACKEND` to `qt5agg`.
    +
    +# Running the Example
    +
    +Run the example using:
    +
    +```
    +python3 -m graph_nav_anchoring_optimization --username USER --password PASSWORD ROBOT_IP
    +```
    +
    +This will load the example map from the data directory, upload it to your robot, and then align it to the provided blueprint.
    +
    +When the example has finished running, it will display an image. The image shows a blueprint. Drawn on top of the blueprint there will be a series of red lines and a series of green lines.
    +
    +The red lines are the anchoring of the map before optimization (this is the default anchoring). The green lines are the anchoring of the map after optimization. The green lines should line up with the hallway in the middle of the blueprint.
    +
    +After the example runs, a new map will have been saved to the data folder containing the optimized anchoring associated with the blueprint.
    +
    +# Understanding the Example Code
    +
    +## Background on Anchorings and Metric Consistency
    +
    +Graph Nav maps are a collection of waypoints and edges. Waypoints define named locations in the world, and edges define how to get from one waypoint to another. Normally, there is no requirement that Graph Nav maps have what is called "metric accuracy," or "metric consistency." That is, there is actually no fixed reference frame that Graph Nav maps can be displayed in.
    +
    +For example, let's suppose we have three waypoints w1 and w2, and w3 connected by the edges (w1, w2), (w2, w3) and (w1, w3). Topologically, this is a triangle:
    +
    +```
    +w1 - w2
    +  \  |
    +    w3
    +```
    +
    +Now, let's suppose the edge (w1, w2) is defined as
    +
    +```
    +from_tform_to = (x = 1, y = 0, z = 0, rotation = identity)
    +```
    +
    +and let's suppose the edge (w1, w3) is:
    +
    +```
    +from_tform_to = (x = 1, y = 1, z = 0, rotation = identity)
    +```
    +
    +And (w2, w3) is:
    +
    +```
    +from_tform_to = (x = -0.1, y = 1.5, z = 0, rotation = identity)
    +```
    +
    +Now, let's suppose we want to determine where all the waypoints are in some fixed reference frame. If we only know about the edge transformations, and arbitrarily assign `w1` to be the origin of our fixed reference frame, we can follow w1 through (w1, w2) to determine that (w2) is at `x=1, y=0, z=0`. But what about w3? The answer actually depends on whether we take the path through (w2, w3) or (w1, w3)!
    +
    +If we take the first path, we would find that w3's coordinates are `x=0.9, y=1.5`. If we take the second path, we find that w3's coordinates are `x = 1, y = 1`. This is because the graph shown above is *metrically inconsistent*.
    +
    +Graph Nav maps normally become metrically inconsistent due to *odometry drift* and inaccurate measurements between waypoints. This is normally okay -- the robot can tolerate a large amount of metric inconsistency while localizing and navigating. However, if you wish to use a Graph Nav map for visualization or creating a high quality map, or registering to existing data, metric inconsistency can make this task very difficult.
    +
    +To solve this problem, Graph Nav provides a concept called *anchorings*. Anchorings are a mapping from waypoint to its pose in a metrically consistent reference frame.  A graph may have many anchorings, for example to a blueprint, BIM model, or point cloud. By providing an anchoring to a graph nav graph, you can more easily display and manipulate Graph Nav maps for your specific application.
    +
    +## Anchoring Optimization
    +
    +The *Map Processing Service* can be used to find metrically consistent anchorings using anchoring optimization, and can be used to align Graph Nav maps to other data sources such as blueprints.
    +
    +In this example, we will show how to use the *Anchoring Optimization Service* to align graph nav maps to a blueprint. Graph Nav maps can be aligned to any data source so long as we have good guesses for where either an April Tag or a specific waypoint is with respect to that data. In this example, we will align an April Tag to a blueprint, and use that as a hint for anchoring optimization -- but you could also align individual waypoints to a blueprint, or use another data source such as a digital twin or BIM model.
    +
    +### Step 1: setting up a connection to the robot
    +
    +The Map Processing Service runs on the robot. Therefore, we will need a connection to the robot, and a lease.
    +
    +
    +```python
    +    # Setup and authenticate the robot.
    +    sdk = bosdyn.client.create_standard_sdk('Anchoring Optimization Example')
    +    robot = sdk.create_robot(options.hostname)
    +    robot.authenticate(options.username, options.password)
    +    _lease_client = robot.ensure_client(LeaseClient.default_service_name)
    +
    +    # We need a lease for the robot to access the map services. This prevents multiple
    +    # clients from fighting over the map data.
    +    _lease_wallet = _lease_client.lease_wallet
    +    _lease = _lease_client.acquire()
    +    _lease_keepalive = LeaseKeepAlive(_lease_client)
    +
    +    # Create clients for graph nav and map processing.
    +    graph_nav_client = robot.ensure_client(GraphNavClient.default_service_name)
    +    map_processing_client = robot.ensure_client(MapProcessingServiceClient.default_service_name)
    +
    +```
    +
    +### Step 2: loading and uploading a graph_nav graph and data
    +
    +The map processing service requires us to upload a graph nav graph and associated snapshot data. The service uses these data to create a metrically consistent anchoring. Maps are stored in the graph nav service, which requires a graph nav client connection. Once inside the graph nav service, maps are accessible to the map processing service.
    +
    +
    +```python
    +    # Load the graph from the disk and upload it to the robot.
    +    (graph, waypoint_snapshots, edge_snapshots) = load_graph_and_snapshots(options.input_map)
    +    upload_graph_and_snapshots(graph_nav_client, graph, waypoint_snapshots, edge_snapshots)
    +
    +```
    +
    +### Step 3: Defining the optimization problem
    +
    +Now that we have a connection to the robot and have loaded the graph and snapshots, we can tell the map processing service to optimize the graph's anchoring.
    +
    +We can provide *parameters* for the optimizer and *hints*. Parameters control how many iterations the optimizer will run, and what data sources it will use for optimization. If no parameters are provided, the optimizer will use reasonable defaults. Hints tell the optimizer information about the anchoring -- for example where a particular April Tag is, or a particular waypoint. If we provide no hints at all, the Map Processing Service will choose an arbitrary waypoint to be the origin.
    +
    +In this case, we will provide a single hint to the service -- the location of a fiducial (April Tag).
    +
    +```python
    +def optimize_anchoring(opt_info, client):
    +    """
    +    Sends an RPC to the robot which optimizes the anchoring and links it to the position of the
    +    fiducial in the blueprint.
    +    :param opt_info: info needed for the optimization.
    +    :param client: the map processing client.
    +    :return: the response to the process_anchoring rpc.
    +    """
    +    initial_hint = map_processing_pb2.AnchoringHint()
    +    object_hint = initial_hint.world_objects.add()
    +    object_hint.object_anchor.id = str(opt_info.fiducial_id)
    +    object_hint.object_anchor.seed_tform_object.CopyFrom(opt_info.get_fiducial_origin())
    +```
    +
    +To get the location of a fiducial, we start with a blueprint image (an example is provided in this example at `data/house_plans.png`). We need to know the relationship between pixels and meters in the image. In this case, the blueprint provides a helpful ruler that tells us the scale -- approximately 49.2 pixels per meter. Once we know this, and we know the location of the fiducial on the blueprint, we can calculate the pose of the fiducial in our desired anchoring frame. NOTE: we will assume that the fiducial is mounted vertically against a wall, with the fiducial "number" upright.
    +
    +```python
    +
    +    def get_fiducial_origin(self):
    +        """
    +        Get an SE3Pose proto defining the origin of the fiducial in the world frame.
    +        The world frame starts at the bottom left of the image, with positive y up, positive x
    +        to the right, and positive z out of the page.
    +        :return: the SE3Pose proto defining the fiducial in this origin.
    +        """
    +        theta = np.deg2rad(self.fiducial_rotation)
    +        # Fiducial frame:
    +        # Assume x is up, and z points out. The rotation matrix
    +        # therefore has x pointed directly out of the page, and
    +        # the zy vectors pointing to the left and up respectively.
    +        # Note that the image origin has z pointing out of the page,
    +        # y up and x to the right.
    +        # Therefore the z axis is equal to (cos(t), sin(t)) and the y axis is
    +        #  (sin(t), -cos(t)).
    +        rot_matrix = np.array([[0, np.sin(theta), np.cos(theta)],
    +                               [0, -np.cos(theta),  np.sin(theta)],
    +                               [1, 0, 0]])
    +        world_tform_fiducial = SE3Pose(rot=Quat.from_matrix(rot_matrix),
    +                                       x=self.fiducial_position[0]/self.pixels_per_meter,
    +                                       y=self.fiducial_position[1]/self.pixels_per_meter,
    +                                       z=0)
    +```
    +
    +By convention, we will assume that the origin of the anchoring is the bottom left of the image, and that the `x` axis is to the right, with the `y` axis up. We will also assume the `z` height of the fiducial is fixed at `z = 0`.  From there, we can determine the position and orientation of the fiducial in 3D space w.r.t the anchoring.
    +
    +We pass this in as an initial hint to the anchoring optimizer, which it will use to align our map to the blueprint (and to ensure that it is metrically consistent).
    +
    +## Running the optimization and interpreting the results
    +We can now send a ProcessAnchoringRequest to the Map Processing Service with our initial guess, and get a result back.
    +```python
    +    return client.process_anchoring(params=map_processing_pb2.ProcessAnchoringRequest.Params(
    +                                        optimize_existing_anchoring=BoolValue(value=False)
    +                                    ),
    +                                    modify_anchoring_on_server=False,
    +                                    stream_intermediate_results=False,
    +                                    initial_hint=initial_hint)
    +```
    +
    +We will choose not to `optimize_existing_anchoring`, `modify_anchoring_on_server` or `stream_intermediate_results` in this example. `modify_anchoring_on_server` changes the anchoring that the robot has internally, `optimize_existing_anchoring` uses the anchoring on the server as an initial guess, and `stream_intermediate_results` will send back partial results at each iteration of the optimization for debugging and visualization purposes.
    +
    +The type of the result is `ProcessAnchoringResponse`. It has a status code, number of iterations, and final cost. If the optimizer failed, or the initial hints were malformed, the optimizer will return a failed status code with some information about why it failed.
    +
    +If optimization succeeds the optimizer returns a new `Anchoring`.
    +
    +```python
    +    # Extract the anchoring from the RPC response.
    +    optimized_anchoring = map_pb2.Anchoring()
    +    for wp in anchoring_response.waypoint_results:
    +        optimized_anchoring.anchors.add().CopyFrom(wp)
    +    for obj in anchoring_response.world_object_results:
    +        optimized_anchoring.objects.add().CopyFrom(obj)
    +```
    +
    +As we can see, an `Anchoring` just consists of a set of waypoints and world objects (for the time being, just April Tags), and the optimized SE3Pose of those waypoints and objects in the anchoring reference frame (in this case, the position/orientation with respect to the lower left corner of the blueprint image).
    +
    +We can now draw the anchorings on the blueprint using `matplotlib`.
    +
    +The image in `data/optimized_anchoring.png` shows the anchoring before optimization (red), and after (green) as a set of lines. Each line is just a line between individual waypoints in the graph which have an edge between them. The fiducial is also shown as two axes, its `z` axis (blue) and its `y` axis (green). We can see it is on the closet door of the upper left bedroom.
    +
    +Note that in the optimized anchoring, the apparent path of the robot is from the upper left bedroom into the living room on the lower right, and then back.
    +
    +## Other ways of viewing anchorings
    +
    +The `view_map.py` example now takes in an argument `-a`, which can be used to draw a map in its anchoring frame. We can also draw the newly optimized map in the anchoring frame after saving it by calling from this directory:
    +
    +`view_map -a ./data/blueprint_example_optimized.walk`
    +
    +An example can be seen in the image stored in this example: `data/optimized_anchoring_viewer.png`, where we can see the point clouds of the map drawn in the anchoring frame.
    +
    +When `-a` is not passed as an argument, the map is shown by chaining edges together starting from an arbitrary origin. As discussed in the first section, this results in an inconsistent drawing.
    +
    +The difference is subtle -- in the unoptimized map, we can see that there is significant height drift between the robot's initial path from the upper left bedroom to the living room and back. This is made most apparent by looking at fiducial 319, which appears in multiple places (with different heights) depending on which waypoint is observing it. In the optimized anchoring, this drift is totally corrected.
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_adonic-nasua-sKXa.YWjefZMJs66n8Ty2Q== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_adonic-nasua-sKXa.YWjefZMJs66n8Ty2Q==
    new file mode 100644
    index 000000000..8991e6acd
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_adonic-nasua-sKXa.YWjefZMJs66n8Ty2Q== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_banner-beaver-e2pGV9dJSvPl0VafeexoCA== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_banner-beaver-e2pGV9dJSvPl0VafeexoCA==
    new file mode 100644
    index 000000000..71db3541a
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_banner-beaver-e2pGV9dJSvPl0VafeexoCA== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_batty-civet-5m4rA3KBlCnrmafzmfwc7w== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_batty-civet-5m4rA3KBlCnrmafzmfwc7w==
    new file mode 100644
    index 000000000..2fee0843a
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_batty-civet-5m4rA3KBlCnrmafzmfwc7w== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_beaten-conger-Y8TNKWPS5AgCnwFxRxEqQw== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_beaten-conger-Y8TNKWPS5AgCnwFxRxEqQw==
    new file mode 100644
    index 000000000..928855054
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_beaten-conger-Y8TNKWPS5AgCnwFxRxEqQw== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_bogus-beaver-I3eVbgCaCS2LQiokS+G26Q== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_bogus-beaver-I3eVbgCaCS2LQiokS+G26Q==
    new file mode 100644
    index 000000000..2c398ba97
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_bogus-beaver-I3eVbgCaCS2LQiokS+G26Q== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_bugged-gaur-CJksgLO.qYXrtgObFJOCag== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_bugged-gaur-CJksgLO.qYXrtgObFJOCag==
    new file mode 100644
    index 000000000..fb601af06
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_bugged-gaur-CJksgLO.qYXrtgObFJOCag== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_bully-midge-PC.BEcgREKIA957iEMHc3g== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_bully-midge-PC.BEcgREKIA957iEMHc3g==
    new file mode 100644
    index 000000000..9af7f2857
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_bully-midge-PC.BEcgREKIA957iEMHc3g== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_bunchy-macaw-3tawwqpSVNpZKk4ZkeDQuQ== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_bunchy-macaw-3tawwqpSVNpZKk4ZkeDQuQ==
    new file mode 100644
    index 000000000..2a54a906f
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_bunchy-macaw-3tawwqpSVNpZKk4ZkeDQuQ== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_camp-howler-QSY.cYTYkj188BxYoDjooA== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_camp-howler-QSY.cYTYkj188BxYoDjooA==
    new file mode 100644
    index 000000000..81e647a8b
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_camp-howler-QSY.cYTYkj188BxYoDjooA== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_choosy-grub-GlpbdQ2sIzEwvBQ6q1pVsg== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_choosy-grub-GlpbdQ2sIzEwvBQ6q1pVsg==
    new file mode 100644
    index 000000000..9f8730c67
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_choosy-grub-GlpbdQ2sIzEwvBQ6q1pVsg== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_corky-fish-axZJYdpz1rcI4h5aWtmXOQ== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_corky-fish-axZJYdpz1rcI4h5aWtmXOQ==
    new file mode 100644
    index 000000000..68ca95806
    --- /dev/null
    +++ b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_corky-fish-axZJYdpz1rcI4h5aWtmXOQ==
    @@ -0,0 +1,2 @@
    +
    +4edge_snapshot_id_corky-fish-axZJYdpz1rcI4h5aWtmXOQ==
    \ No newline at end of file
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_crass-hornet-XkT1qVpleSSX6kxYYUf4Wg== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_crass-hornet-XkT1qVpleSSX6kxYYUf4Wg==
    new file mode 100644
    index 000000000..f981850c8
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_crass-hornet-XkT1qVpleSSX6kxYYUf4Wg== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_dovish-mule-sLOw1RIvzhyns6LNe3ob.w== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_dovish-mule-sLOw1RIvzhyns6LNe3ob.w==
    new file mode 100644
    index 000000000..d85e73310
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_dovish-mule-sLOw1RIvzhyns6LNe3ob.w== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_finer-drum-lfeMvp0.PjV8bnWkXdhmtw== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_finer-drum-lfeMvp0.PjV8bnWkXdhmtw==
    new file mode 100644
    index 000000000..7f58aaef4
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_finer-drum-lfeMvp0.PjV8bnWkXdhmtw== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_fizzy-boxer-L+9DF7bW1zcfTwdIH.g64Q== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_fizzy-boxer-L+9DF7bW1zcfTwdIH.g64Q==
    new file mode 100644
    index 000000000..6b89a4793
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_fizzy-boxer-L+9DF7bW1zcfTwdIH.g64Q== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_floral-ewe-gKjlcPPh2jgkDYmmag0XYg== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_floral-ewe-gKjlcPPh2jgkDYmmag0XYg==
    new file mode 100644
    index 000000000..a0fa045fe
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_floral-ewe-gKjlcPPh2jgkDYmmag0XYg== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_mangy-oxen-2PQpILxNrgZMnQ8yTlvGDA== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_mangy-oxen-2PQpILxNrgZMnQ8yTlvGDA==
    new file mode 100644
    index 000000000..101e89317
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_mangy-oxen-2PQpILxNrgZMnQ8yTlvGDA== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_mazed-pig-RJ38f+eC.IAZXO87klZ0VQ== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_mazed-pig-RJ38f+eC.IAZXO87klZ0VQ==
    new file mode 100644
    index 000000000..cfe029a3a
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_mazed-pig-RJ38f+eC.IAZXO87klZ0VQ== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_mazed-skink-8Mbd1dKEBtPD+Qz3Am+jFw== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_mazed-skink-8Mbd1dKEBtPD+Qz3Am+jFw==
    new file mode 100644
    index 000000000..6de3f0db0
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_mazed-skink-8Mbd1dKEBtPD+Qz3Am+jFw== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_motile-ermine-8zIqf8ILfQOUOlzPlcbtgw== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_motile-ermine-8zIqf8ILfQOUOlzPlcbtgw==
    new file mode 100644
    index 000000000..a5d3bc143
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_motile-ermine-8zIqf8ILfQOUOlzPlcbtgw== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_nary-drum-Ygf1gHeY4PVQy2Suc4P5LA== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_nary-drum-Ygf1gHeY4PVQy2Suc4P5LA==
    new file mode 100644
    index 000000000..fb1724991
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_nary-drum-Ygf1gHeY4PVQy2Suc4P5LA== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_passe-baboon-q3lvB0hlkxhNahRsx5s4Cg== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_passe-baboon-q3lvB0hlkxhNahRsx5s4Cg==
    new file mode 100644
    index 000000000..1c749d3a3
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_passe-baboon-q3lvB0hlkxhNahRsx5s4Cg== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_peeled-locust-fKPRTVwWj.7GqDi+LWq6Kg== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_peeled-locust-fKPRTVwWj.7GqDi+LWq6Kg==
    new file mode 100644
    index 000000000..8036bbef0
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_peeled-locust-fKPRTVwWj.7GqDi+LWq6Kg== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_roofed-vole-VYboM9eC7p3dGioBB5L6gA== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_roofed-vole-VYboM9eC7p3dGioBB5L6gA==
    new file mode 100644
    index 000000000..e43de0c52
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_roofed-vole-VYboM9eC7p3dGioBB5L6gA== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_rotary-simian-0rQJ8NBu5ad1.ZisaA4SUQ== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_rotary-simian-0rQJ8NBu5ad1.ZisaA4SUQ==
    new file mode 100644
    index 000000000..ebc6ff689
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_rotary-simian-0rQJ8NBu5ad1.ZisaA4SUQ== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_seared-heron-QxT18TjCj+yOZZgJnmvK2Q== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_seared-heron-QxT18TjCj+yOZZgJnmvK2Q==
    new file mode 100644
    index 000000000..583e9b37a
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_seared-heron-QxT18TjCj+yOZZgJnmvK2Q== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_sedgy-magpie-pcPeE8kWC0r.xixRkFY69w== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_sedgy-magpie-pcPeE8kWC0r.xixRkFY69w==
    new file mode 100644
    index 000000000..6d989d4d9
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_sedgy-magpie-pcPeE8kWC0r.xixRkFY69w== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_sheeny-lemur-AnxYiporTPlHN4Zb41afKg== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_sheeny-lemur-AnxYiporTPlHN4Zb41afKg==
    new file mode 100644
    index 000000000..f11c3a2e0
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_sheeny-lemur-AnxYiporTPlHN4Zb41afKg== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_stone-weasel-axcOf8iM.LmrM0dAJYtkuA== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_stone-weasel-axcOf8iM.LmrM0dAJYtkuA==
    new file mode 100644
    index 000000000..a29ea5896
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_stone-weasel-axcOf8iM.LmrM0dAJYtkuA== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_tasty-dingo-gZB7jqcHex9YbhAbWm35eA== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_tasty-dingo-gZB7jqcHex9YbhAbWm35eA==
    new file mode 100644
    index 000000000..e054451af
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_tasty-dingo-gZB7jqcHex9YbhAbWm35eA== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_togged-hyena-O2w31HDhXGUZvzOZNXsA6Q== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_togged-hyena-O2w31HDhXGUZvzOZNXsA6Q==
    new file mode 100644
    index 000000000..519a7fc29
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_togged-hyena-O2w31HDhXGUZvzOZNXsA6Q== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_tusked-dugong-ccHuXEzBQapj43R2IBTQoA== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_tusked-dugong-ccHuXEzBQapj43R2IBTQoA==
    new file mode 100644
    index 000000000..eb7bb90c0
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_tusked-dugong-ccHuXEzBQapj43R2IBTQoA== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_two-hare-jUR0qoAO2g+mScS6YEHGOw== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_two-hare-jUR0qoAO2g+mScS6YEHGOw==
    new file mode 100644
    index 000000000..8e5f5d45a
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_two-hare-jUR0qoAO2g+mScS6YEHGOw== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_unsent-shark-C6oPiXQZAGZoOu4k1.pUaA== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_unsent-shark-C6oPiXQZAGZoOu4k1.pUaA==
    new file mode 100644
    index 000000000..1940262db
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_unsent-shark-C6oPiXQZAGZoOu4k1.pUaA== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_unwell-impala-xzLgNOj14M+z.Qkfp7Zp5A== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_unwell-impala-xzLgNOj14M+z.Qkfp7Zp5A==
    new file mode 100644
    index 000000000..70828e2c6
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_unwell-impala-xzLgNOj14M+z.Qkfp7Zp5A== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_upwind-mare-MwzevF8HFfJMWxokix1FZQ== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_upwind-mare-MwzevF8HFfJMWxokix1FZQ==
    new file mode 100644
    index 000000000..533171bc4
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_upwind-mare-MwzevF8HFfJMWxokix1FZQ== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_whiny-mare-t.3McR12bHVCVQLgUnnn8Q== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_whiny-mare-t.3McR12bHVCVQLgUnnn8Q==
    new file mode 100644
    index 000000000..d21f57ab6
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/edge_snapshots/edge_snapshot_id_whiny-mare-t.3McR12bHVCVQLgUnnn8Q== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/graph b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/graph
    new file mode 100644
    index 000000000..2ba6ef41b
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/graph differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/missions/autogenerated b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/missions/autogenerated
    new file mode 100644
    index 000000000..ee6a82e09
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/missions/autogenerated differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_bigger-ermine-.c8RDx162GEqOMeS6vH.CA== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_bigger-ermine-.c8RDx162GEqOMeS6vH.CA==
    new file mode 100644
    index 000000000..47f9d2e26
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_bigger-ermine-.c8RDx162GEqOMeS6vH.CA== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_campy-lamb-en8VhZAXJua5.eBaFZlDOA== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_campy-lamb-en8VhZAXJua5.eBaFZlDOA==
    new file mode 100644
    index 000000000..540705f8c
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_campy-lamb-en8VhZAXJua5.eBaFZlDOA== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_cloggy-cows-CbYl.ZCmf8cFe.TFiR.ncw== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_cloggy-cows-CbYl.ZCmf8cFe.TFiR.ncw==
    new file mode 100644
    index 000000000..190db213e
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_cloggy-cows-CbYl.ZCmf8cFe.TFiR.ncw== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_cranky-coati-8lMg7U4O5+vvdX2iQzkjVQ== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_cranky-coati-8lMg7U4O5+vvdX2iQzkjVQ==
    new file mode 100644
    index 000000000..2803d98a4
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_cranky-coati-8lMg7U4O5+vvdX2iQzkjVQ== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_darned-mammut-zZ+ol73v0lHz4LtPJ.u+gw== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_darned-mammut-zZ+ol73v0lHz4LtPJ.u+gw==
    new file mode 100644
    index 000000000..05d2da40a
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_darned-mammut-zZ+ol73v0lHz4LtPJ.u+gw== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_dented-goose-8y9zaNO8CZEzbk.co.Nphw== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_dented-goose-8y9zaNO8CZEzbk.co.Nphw==
    new file mode 100644
    index 000000000..6231aed9a
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_dented-goose-8y9zaNO8CZEzbk.co.Nphw== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_dimmed-finch-FBpLPtt..7+s1Kdu5jBVcw== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_dimmed-finch-FBpLPtt..7+s1Kdu5jBVcw==
    new file mode 100644
    index 000000000..fff2f18c0
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_dimmed-finch-FBpLPtt..7+s1Kdu5jBVcw== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_doughy-turtle-K4RXOEe857BAEDrYcA3iUQ== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_doughy-turtle-K4RXOEe857BAEDrYcA3iUQ==
    new file mode 100644
    index 000000000..62e8b31fb
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_doughy-turtle-K4RXOEe857BAEDrYcA3iUQ== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_dyed-grouse-.xn4zz2B+AN57HkRgULDKw== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_dyed-grouse-.xn4zz2B+AN57HkRgULDKw==
    new file mode 100644
    index 000000000..8b76dd843
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_dyed-grouse-.xn4zz2B+AN57HkRgULDKw== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_eased-trout-h5+MCFL63gvb.KCJeWIjig== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_eased-trout-h5+MCFL63gvb.KCJeWIjig==
    new file mode 100644
    index 000000000..bbd979d8e
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_eased-trout-h5+MCFL63gvb.KCJeWIjig== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_extant-beaver-xP+FrPSKIKYvIAF1uA0fdA== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_extant-beaver-xP+FrPSKIKYvIAF1uA0fdA==
    new file mode 100644
    index 000000000..f5cb54c05
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_extant-beaver-xP+FrPSKIKYvIAF1uA0fdA== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_gauche-lizard-iJ1mP80UWWiry6S3wRmrTA== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_gauche-lizard-iJ1mP80UWWiry6S3wRmrTA==
    new file mode 100644
    index 000000000..18c40a460
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_gauche-lizard-iJ1mP80UWWiry6S3wRmrTA== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_grumpy-budgie-MunV.QYh3e9UIxAKafTS6Q== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_grumpy-budgie-MunV.QYh3e9UIxAKafTS6Q==
    new file mode 100644
    index 000000000..fff049686
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_grumpy-budgie-MunV.QYh3e9UIxAKafTS6Q== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_hammy-llama-eKiHDCjWpUC4xtdXmuZVgQ== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_hammy-llama-eKiHDCjWpUC4xtdXmuZVgQ==
    new file mode 100644
    index 000000000..8d9105e79
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_hammy-llama-eKiHDCjWpUC4xtdXmuZVgQ== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_hick-buck-NyRMSxxeNBipdKZ2s0SkrA== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_hick-buck-NyRMSxxeNBipdKZ2s0SkrA==
    new file mode 100644
    index 000000000..0a9c01068
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_hick-buck-NyRMSxxeNBipdKZ2s0SkrA== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_hooved-porgy-T5AV48L969bwTYhDe+TpSA== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_hooved-porgy-T5AV48L969bwTYhDe+TpSA==
    new file mode 100644
    index 000000000..0b5dcb461
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_hooved-porgy-T5AV48L969bwTYhDe+TpSA== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_human-barker-1HNmV5g+LhBqhtE4lcLatA== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_human-barker-1HNmV5g+LhBqhtE4lcLatA==
    new file mode 100644
    index 000000000..e07ee878c
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_human-barker-1HNmV5g+LhBqhtE4lcLatA== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_iron-oryx-3CrSlIsoWrww4m2QOJewAA== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_iron-oryx-3CrSlIsoWrww4m2QOJewAA==
    new file mode 100644
    index 000000000..5d08c33c3
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_iron-oryx-3CrSlIsoWrww4m2QOJewAA== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_ivied-gull-IQ.XUOXrqZ72wHdzJUEOqg== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_ivied-gull-IQ.XUOXrqZ72wHdzJUEOqg==
    new file mode 100644
    index 000000000..2c83a875f
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_ivied-gull-IQ.XUOXrqZ72wHdzJUEOqg== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_joyous-gnu-1nZxbDYqwVYbLKLo3JwLcg== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_joyous-gnu-1nZxbDYqwVYbLKLo3JwLcg==
    new file mode 100644
    index 000000000..c8178f241
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_joyous-gnu-1nZxbDYqwVYbLKLo3JwLcg== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_lanate-bowfin-0DpTtzsG0DSfY2fjFhMJxg== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_lanate-bowfin-0DpTtzsG0DSfY2fjFhMJxg==
    new file mode 100644
    index 000000000..7ebf2665b
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_lanate-bowfin-0DpTtzsG0DSfY2fjFhMJxg== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_legged-angus-Q4Iih+aZZb4bQyGb8BMK2A== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_legged-angus-Q4Iih+aZZb4bQyGb8BMK2A==
    new file mode 100644
    index 000000000..21d0ade3d
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_legged-angus-Q4Iih+aZZb4bQyGb8BMK2A== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_lilac-fawn-DhAd3XaQByaAE1oz0H2iAw== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_lilac-fawn-DhAd3XaQByaAE1oz0H2iAw==
    new file mode 100644
    index 000000000..2635c079c
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_lilac-fawn-DhAd3XaQByaAE1oz0H2iAw== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_lobed-porgy-y0kzVJJ.OqL2ep0tiZgdQA== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_lobed-porgy-y0kzVJJ.OqL2ep0tiZgdQA==
    new file mode 100644
    index 000000000..0b11846f7
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_lobed-porgy-y0kzVJJ.OqL2ep0tiZgdQA== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_magic-snipe-AaYOcqAn2YKu9mOAeJRzWQ== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_magic-snipe-AaYOcqAn2YKu9mOAeJRzWQ==
    new file mode 100644
    index 000000000..c018ed7cc
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_magic-snipe-AaYOcqAn2YKu9mOAeJRzWQ== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_matte-bedbug-kM21WvJhjJYvw4X5+aJrpw== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_matte-bedbug-kM21WvJhjJYvw4X5+aJrpw==
    new file mode 100644
    index 000000000..a40876459
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_matte-bedbug-kM21WvJhjJYvw4X5+aJrpw== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_mazed-redbug-PblRRf2uVWL8tmjlHN8mHw== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_mazed-redbug-PblRRf2uVWL8tmjlHN8mHw==
    new file mode 100644
    index 000000000..681cbbf92
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_mazed-redbug-PblRRf2uVWL8tmjlHN8mHw== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_puff-taipan-RB3TQaCPhKfXiMn5db+vvA== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_puff-taipan-RB3TQaCPhKfXiMn5db+vvA==
    new file mode 100644
    index 000000000..8c81786c2
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_puff-taipan-RB3TQaCPhKfXiMn5db+vvA== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_repand-goat-dWcu.LunhDJPRymB2yZs4Q== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_repand-goat-dWcu.LunhDJPRymB2yZs4Q==
    new file mode 100644
    index 000000000..be61d7aa6
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_repand-goat-dWcu.LunhDJPRymB2yZs4Q== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_rotary-goby-f9dAsb1jHH2Xk7r8fjY6.Q== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_rotary-goby-f9dAsb1jHH2Xk7r8fjY6.Q==
    new file mode 100644
    index 000000000..c5d91fc8a
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_rotary-goby-f9dAsb1jHH2Xk7r8fjY6.Q== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_rusted-corgi-tKVfPTXn7izQ2QQqZuxngw== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_rusted-corgi-tKVfPTXn7izQ2QQqZuxngw==
    new file mode 100644
    index 000000000..d9413b061
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_rusted-corgi-tKVfPTXn7izQ2QQqZuxngw== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_scared-craw-7mbUX6HNQ0kvKeKEw0fCHQ== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_scared-craw-7mbUX6HNQ0kvKeKEw0fCHQ==
    new file mode 100644
    index 000000000..acad21172
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_scared-craw-7mbUX6HNQ0kvKeKEw0fCHQ== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_sheeny-goat-cJbIHkbkUrafzkt3+q2VEg== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_sheeny-goat-cJbIHkbkUrafzkt3+q2VEg==
    new file mode 100644
    index 000000000..d2249697d
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_sheeny-goat-cJbIHkbkUrafzkt3+q2VEg== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_stubby-canine-bUJEAaddmal.PL5xcyftNQ== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_stubby-canine-bUJEAaddmal.PL5xcyftNQ==
    new file mode 100644
    index 000000000..d342e9954
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_stubby-canine-bUJEAaddmal.PL5xcyftNQ== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_sunlit-tomcat-mBDDaORmppUwESNBzKUc6A== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_sunlit-tomcat-mBDDaORmppUwESNBzKUc6A==
    new file mode 100644
    index 000000000..c7f26336d
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_sunlit-tomcat-mBDDaORmppUwESNBzKUc6A== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_tiptoe-ocelot-morhmATttW53w+JCSu4edw== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_tiptoe-ocelot-morhmATttW53w+JCSu4edw==
    new file mode 100644
    index 000000000..cb973ca6e
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_tiptoe-ocelot-morhmATttW53w+JCSu4edw== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_visual-equid-p39HyP56uN5ETFIbI+ss1Q== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_visual-equid-p39HyP56uN5ETFIbI+ss1Q==
    new file mode 100644
    index 000000000..b189b0b7a
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_visual-equid-p39HyP56uN5ETFIbI+ss1Q== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_wedded-syphon-nveqK13lh.lKz3caNfKgKA== b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_wedded-syphon-nveqK13lh.lKz3caNfKgKA==
    new file mode 100644
    index 000000000..5a158565a
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/blueprint_example.walk/waypoint_snapshots/snapshot_wedded-syphon-nveqK13lh.lKz3caNfKgKA== differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/house_plans.png b/python/examples/graph_nav_anchoring_optimization/data/house_plans.png
    new file mode 100644
    index 000000000..4331d7d1f
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/house_plans.png differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/optimized_anchoring.png b/python/examples/graph_nav_anchoring_optimization/data/optimized_anchoring.png
    new file mode 100644
    index 000000000..8c11cf9d7
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/optimized_anchoring.png differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/data/optimized_anchoring_viewer.png b/python/examples/graph_nav_anchoring_optimization/data/optimized_anchoring_viewer.png
    new file mode 100644
    index 000000000..c8d10740e
    Binary files /dev/null and b/python/examples/graph_nav_anchoring_optimization/data/optimized_anchoring_viewer.png differ
    diff --git a/python/examples/graph_nav_anchoring_optimization/graph_nav_anchoring_optimization.py b/python/examples/graph_nav_anchoring_optimization/graph_nav_anchoring_optimization.py
    new file mode 100644
    index 000000000..076f20e0e
    --- /dev/null
    +++ b/python/examples/graph_nav_anchoring_optimization/graph_nav_anchoring_optimization.py
    @@ -0,0 +1,307 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +import argparse
    +import sys
    +
    +import bosdyn
    +import matplotlib.pyplot as plt
    +import matplotlib.image as plt_img
    +import numpy as np
    +
    +import os
    +
    +from bosdyn.api import geometry_pb2
    +from bosdyn.api.graph_nav import map_pb2, map_processing_pb2
    +from bosdyn.client.graph_nav import GraphNavClient
    +from bosdyn.client.lease import LeaseClient, LeaseKeepAlive
    +from bosdyn.client.map_processing import MapProcessingServiceClient
    +from bosdyn.client.math_helpers import SE3Pose, Quat
    +from bosdyn.client import util
    +from google.protobuf.wrappers_pb2 import BoolValue
    +
    +
    +class OptInfo:
    +    def __init__(self, fiducial_id, pixels_per_meter, fiducial_position, fiducial_rotation):
    +        """
    +        Info needed to run the optimization.
    +        :param fiducial_id: The ID of the fiducial to constrain to the blueprint.
    +        :param pixels_per_meter: The number of pixels in a meter in the blueprint.
    +        :param fiducial_position: a tuple containing the pixel location of the fiducial.
    +        :param fiducial_rotation: rotation in degrees of the fiducial on the blueprint page.
    +        """
    +        self.pixels_per_meter = pixels_per_meter
    +        self.fiducial_position = fiducial_position
    +        self.fiducial_rotation = fiducial_rotation
    +        self.fiducial_id = fiducial_id
    +
    +    def get_fiducial_origin(self):
    +        """
    +        Get an SE3Pose proto defining the origin of the fiducial in the world frame.
    +        The world frame starts at the bottom left of the image, with positive y up, positive x
    +        to the right, and positive z out of the page.
    +        :return: the SE3Pose proto defining the fiducial in this origin.
    +        """
    +        theta = np.deg2rad(self.fiducial_rotation)
    +        # Fiducial frame:
    +        # Assume x is up, and z points out. The rotation matrix
    +        # therefore has x pointed directly out of the page, and
    +        # the zy vectors pointing to the left and up respectively.
    +        # Note that the image origin has z pointing out of the page,
    +        # y up and x to the right.
    +        # Therefore the z axis is equal to (cos(t), sin(t)) and the y axis is
    +        #  (sin(t), -cos(t)).
    +        rot_matrix = np.array([[0, np.sin(theta), np.cos(theta)],
    +                               [0, -np.cos(theta),  np.sin(theta)],
    +                               [1, 0, 0]])
    +        world_tform_fiducial = SE3Pose(rot=Quat.from_matrix(rot_matrix),
    +                                       x=self.fiducial_position[0]/self.pixels_per_meter,
    +                                       y=self.fiducial_position[1]/self.pixels_per_meter,
    +                                       z=0)
    +        return world_tform_fiducial.to_proto()
    +
    +    def meter_to_pixel(self, pos):
    +        """Converts a proto with pos.x, pos.y in meters to a tuple of (x,y) pixels."""
    +        return (pos.x * self.pixels_per_meter, pos.y * self.pixels_per_meter)
    +
    +def get_current_directory():
    +    """
    +    Get the directory of the script.
    +    """
    +    return os.path.dirname(os.path.realpath(__file__))
    +
    +def get_data_directory():
    +    """
    +    Get the local data directory.
    +    """
    +    return os.path.join(get_current_directory(), 'data')
    +
    +def show_blueprint(path):
    +    """
    +    Loads an image file and plots it.
    +    :param path: the full path to the image file.
    +    """
    +    img = plt_img.imread(path)
    +    # We want a coordinate system that is right handed, with y going up,
    +    # and x to the right, with z pointing out of the page. This requires us
    +    # to flip the image vertically, and set the origin of the plot to the lower
    +    # left corner. This is because images in matplotlib are plotted from the top
    +    # left corner as (0,0), with x going to the right and y down.
    +    img = np.flipud(img)
    +    plt.imshow(img, origin='lower')
    +
    +def load_graph_and_snapshots(filepath):
    +    """
    +    Load the graph and snapshots from a directory.
    +    :param filepath: The full file path to the directory.
    +    :return: a tuple containing the graph, waypoint snapshots and edge snapshots.
    +    """
    +    print("Loading the graph from disk into local storage at {}".format(filepath))
    +    graph = map_pb2.Graph()
    +    waypoint_snapshots = {}
    +    edge_snapshots = {}
    +    with open(os.path.join(filepath, "graph"), "rb") as graph_file:
    +        # Load the graph from disk.
    +        data = graph_file.read()
    +        graph.ParseFromString(data)
    +        print("Loaded graph has {} waypoints and {} edges".format(
    +            len(graph.waypoints), len(graph.edges)))
    +    for waypoint in graph.waypoints:
    +        if len(waypoint.snapshot_id) == 0:
    +            continue
    +        # Load the waypoint snapshots from disk.
    +        with open(os.path.join(filepath, "waypoint_snapshots", waypoint.snapshot_id),
    +                  "rb") as snapshot_file:
    +            waypoint_snapshot = map_pb2.WaypointSnapshot()
    +            waypoint_snapshot.ParseFromString(snapshot_file.read())
    +            waypoint_snapshots[waypoint_snapshot.id] = waypoint_snapshot
    +    for edge in graph.edges:
    +        if len(edge.snapshot_id) == 0:
    +            continue
    +        # Load the edge snapshots from disk.
    +        with open(os.path.join(filepath, "edge_snapshots", edge.snapshot_id), "rb") as snapshot_file:
    +            edge_snapshot = map_pb2.EdgeSnapshot()
    +            edge_snapshot.ParseFromString(snapshot_file.read())
    +            edge_snapshots[edge_snapshot.id] = edge_snapshot
    +
    +    return (graph, waypoint_snapshots, edge_snapshots)
    +
    +def save_graph_and_snapshots(filepath, graph, waypoint_snapshots, edge_snapshots):
    +    """
    +    Save the full graph nav map to disk.
    +    :param filepath: Directory (will be created) for the map.
    +    :param graph: the graph of waypoints and edges.
    +    :param waypoint_snapshots: Large data associated with waypoints.
    +    :param edge_snapshots: Large data associated with edges.
    +    """
    +    print("Saving the graph to local storage at {}".format(filepath))
    +    os.makedirs(filepath, exist_ok=True)
    +    os.makedirs(os.path.join(filepath, "waypoint_snapshots"), exist_ok=True)
    +    os.makedirs(os.path.join(filepath, "edge_snapshots"), exist_ok=True)
    +    with open(os.path.join(filepath, "graph"), "wb") as graph_file:
    +        graph_file.write(graph.SerializeToString())
    +    for snapshot_id, waypoint_snapshot in waypoint_snapshots.items():
    +        # Save the waypoint snapshots to disk.
    +        with open(os.path.join(filepath, "waypoint_snapshots", snapshot_id),
    +                  "wb") as snapshot_file:
    +            snapshot_file.write(waypoint_snapshot.SerializeToString())
    +    for snapshot_id, edge_snapshot in edge_snapshots.items():
    +        # Save the edge snapshots to disk.
    +        with open(os.path.join(filepath, "edge_snapshots", snapshot_id),
    +                  "wb") as snapshot_file:
    +            snapshot_file.write(edge_snapshot.SerializeToString())
    +
    +def show_fiducial_origin(opt_info):
    +    """
    +    Draws the fiducial as a frame on the map.
    +    :param opt_info: info about the optimization.
    +    """
    +    VEC_LENGTH = 30.0
    +    world_T_fiducial= opt_info.get_fiducial_origin()
    +    world_T_fiducial_mat = SE3Pose.from_obj(world_T_fiducial).rotation.to_matrix()
    +    plt.plot([world_T_fiducial.position.x * opt_info.pixels_per_meter, world_T_fiducial.position.x * opt_info.pixels_per_meter + world_T_fiducial_mat[0, 2] * VEC_LENGTH],
    +             [world_T_fiducial.position.y * opt_info.pixels_per_meter, world_T_fiducial.position.y * opt_info.pixels_per_meter + world_T_fiducial_mat[1, 2] * VEC_LENGTH], 'b-')
    +
    +    plt.plot([world_T_fiducial.position.x * opt_info.pixels_per_meter, world_T_fiducial.position.x * opt_info.pixels_per_meter + world_T_fiducial_mat[0, 1] * VEC_LENGTH],
    +             [world_T_fiducial.position.y * opt_info.pixels_per_meter, world_T_fiducial.position.y * opt_info.pixels_per_meter + world_T_fiducial_mat[1, 1] * VEC_LENGTH], 'g-')
    +
    +
    +def draw_graph(opt_info, graph, color, anchoring):
    +    """
    +    Draws the graph with the given anchoring on the blueprint.
    +    :param opt_info: Info about the optimization process.
    +    :param graph: The graph of waypoints and edges.
    +    :param color: matplotlib color string.
    +    :param anchoring: The anchoring to use.
    +    """
    +    waypoint_id_to_waypoint = {}
    +    waypoint_id_to_anchoring = {}
    +    for wp in graph.waypoints:
    +        waypoint_id_to_waypoint[wp.id] = wp
    +    for wp in anchoring.anchors:
    +        waypoint_id_to_anchoring[wp.id] = wp
    +    for edge in graph.edges:
    +        anchor_from = waypoint_id_to_anchoring[edge.id.from_waypoint]
    +        anchor_to = waypoint_id_to_anchoring[edge.id.to_waypoint]
    +        pos_from = opt_info.meter_to_pixel(anchor_from.seed_tform_waypoint.position)
    +        pos_to = opt_info.meter_to_pixel(anchor_to.seed_tform_waypoint.position)
    +        plt.plot([pos_from[0], pos_to[0]], [pos_from[1], pos_to[1]], color)
    +
    +def upload_graph_and_snapshots(client, graph, waypoint_snapshots, edge_snapshots):
    +    """
    +    Upload the graph nav map to the robot.
    +    :param client: the graph nav client.
    +    :param graph: The graph of waypoints and edges.
    +    :param waypoint_snapshots: large data associated with waypoints.
    +    :param edge_snapshots: large data associated with edges.
    +    """
    +    # Upload the graph to the robot.
    +    print("Uploading the graph and snapshots to the robot...")
    +    response = client.upload_graph(lease=None, graph=graph, generate_new_anchoring=False)
    +    # Upload the snapshots to the robot.
    +    for snapshot_id in response.unknown_waypoint_snapshot_ids:
    +        if snapshot_id not in waypoint_snapshots:
    +            continue
    +        waypoint_snapshot = waypoint_snapshots[snapshot_id]
    +        client.upload_waypoint_snapshot(waypoint_snapshot)
    +        print("Uploaded {}".format(waypoint_snapshot.id))
    +    for snapshot_id in response.unknown_edge_snapshot_ids:
    +        if snapshot_id not in edge_snapshots:
    +            continue
    +        edge_snapshot = edge_snapshots[snapshot_id]
    +        client.upload_edge_snapshot(edge_snapshot)
    +        print("Uploaded {}".format(edge_snapshot.id))
    +
    +def optimize_anchoring(opt_info, client):
    +    """
    +    Sends an RPC to the robot which optimizes the anchoring and links it to the position of the
    +    fiducial in the blueprint.
    +    :param opt_info: info needed for the optimization.
    +    :param client: the map processing client.
    +    :return: the response to the process_anchoring rpc.
    +    """
    +    initial_hint = map_processing_pb2.AnchoringHint()
    +    object_hint = initial_hint.world_objects.add()
    +    object_hint.object_anchor.id = str(opt_info.fiducial_id)
    +    object_hint.object_anchor.seed_tform_object.CopyFrom(opt_info.get_fiducial_origin())
    +    return client.process_anchoring(params=map_processing_pb2.ProcessAnchoringRequest.Params(
    +                                        optimize_existing_anchoring=BoolValue(value=False)
    +                                    ),
    +                                    modify_anchoring_on_server=False,
    +                                    stream_intermediate_results=False,
    +                                    initial_hint=initial_hint)
    +
    +
    +def main(argv):
    +    """Run the command-line interface."""
    +    parser = argparse.ArgumentParser(description=__doc__)
    +    parser.add_argument('-i', '--input-map',
    +                        help='Full filepath of the map directory.',
    +                        default=os.path.join(get_data_directory(), 'blueprint_example.walk'))
    +    parser.add_argument('-o', '--output-map', help='Full filepath of the directory to save the optimized map to.',
    +                        default=os.path.join(get_data_directory(), 'blueprint_example_optimized.walk'))
    +    parser.add_argument('-b', '--blueprint', help='Full filepath to a blueprint image. Should be a raster type, like jpg, png, etc. pdf/svg etc. types are not supported.',
    +                        default=os.path.join(get_data_directory(), 'house_plans.png'))
    +    parser.add_argument('-p', '--pixels-per-meter', type=float, help='In the blueprint, the number of pixels in a meter.',
    +                        default=49.2) # This default is taken from the scale on the example blueprint.
    +    parser.add_argument('-f', '--fiducial-id', type=int, help='The ID of the fiducial to constrain to the blueprint.', default=320)
    +    parser.add_argument('-fx', '--fiducial-position-pixels-x', type=float,
    +                        help='The x (left-right) position in the blueprint of the fiducial in pixels from the bottom left corner.',
    +                        default=168)
    +    parser.add_argument('-fy', '--fiducial-position-pixels-y', type=float,
    +                        help='The y (up-down) position in the blueprint of the fiducial in pixels from the bottom left corner.',
    +                        default=920)
    +    parser.add_argument('-fr', '--fiducial-rotation-degrees', type=float, default=180.0,
    +                        help='The rotation of the fiducial, assuming 0 degrees is pointing to the right. The fiducial will be assumed to be vertically mounted on a wall, perfectly orthogonal to the ground.')
    +
    +    bosdyn.client.util.add_common_arguments(parser)
    +    options = parser.parse_args(argv)
    +
    +    opt_info = OptInfo(fiducial_id=options.fiducial_id,
    +                       pixels_per_meter=options.pixels_per_meter,
    +                       fiducial_position=(options.fiducial_position_pixels_x, options.fiducial_position_pixels_y), fiducial_rotation=options.fiducial_rotation_degrees)
    +
    +    # Setup and authenticate the robot.
    +    sdk = bosdyn.client.create_standard_sdk('GraphNavClient')
    +    robot = sdk.create_robot(options.hostname)
    +    robot.authenticate(options.username, options.password)
    +    _lease_client = robot.ensure_client(LeaseClient.default_service_name)
    +
    +    # We need a lease for the robot to access the map services. This prevents multiple
    +    # clients from fighting over the map data.
    +    _lease_wallet = _lease_client.lease_wallet
    +    _lease = _lease_client.acquire()
    +    _lease_keepalive = LeaseKeepAlive(_lease_client)
    +
    +    (graph, waypoint_snapshots, edge_snapshots) = load_graph_and_snapshots(options.input_map)
    +    graph_nav_client = robot.ensure_client(GraphNavClient.default_service_name)
    +    map_processing_client = robot.ensure_client(MapProcessingServiceClient.default_service_name)
    +    upload_graph_and_snapshots(graph_nav_client, graph, waypoint_snapshots, edge_snapshots)
    +
    +    print("Optimizing...")
    +    anchoring_response = optimize_anchoring(opt_info, map_processing_client)
    +    print("Status: {}, Iterations: {}, Cost: {}".format(anchoring_response.status, anchoring_response.iteration, anchoring_response.cost))
    +
    +    # Extract the anchoring from the RPC response.
    +    optimized_anchoring = map_pb2.Anchoring()
    +    for wp in anchoring_response.waypoint_results:
    +        optimized_anchoring.anchors.add().CopyFrom(wp)
    +    for obj in anchoring_response.world_object_results:
    +        optimized_anchoring.objects.add().CopyFrom(obj)
    +
    +    # Plot the results.
    +    show_blueprint(options.blueprint)
    +    show_fiducial_origin(opt_info)
    +    draw_graph(opt_info, graph, 'r-', graph.anchoring)
    +    draw_graph(opt_info, graph, 'g-', optimized_anchoring)
    +
    +    # Apply the new anchoring.
    +    graph.anchoring.CopyFrom(optimized_anchoring)
    +    # Save the optimized graph.
    +    save_graph_and_snapshots(options.output_map, graph, waypoint_snapshots, edge_snapshots)
    +    plt.show()
    +
    +main(sys.argv[1:])
    diff --git a/python/examples/graph_nav_anchoring_optimization/requirements.txt b/python/examples/graph_nav_anchoring_optimization/requirements.txt
    new file mode 100644
    index 000000000..afbd0149e
    --- /dev/null
    +++ b/python/examples/graph_nav_anchoring_optimization/requirements.txt
    @@ -0,0 +1,4 @@
    +-f ../../../prebuilt
    +
    +bosdyn-client >= 2.0
    +matplotlib >= 3.0
    diff --git a/python/examples/graph_nav_command_line/graph_nav_command_line.py b/python/examples/graph_nav_command_line/graph_nav_command_line.py
    index 946a055e3..970068532 100644
    --- a/python/examples/graph_nav_command_line/graph_nav_command_line.py
    +++ b/python/examples/graph_nav_command_line/graph_nav_command_line.py
    @@ -26,6 +26,7 @@
     from bosdyn.client.graph_nav import GraphNavClient
     from bosdyn.client.frame_helpers import get_odom_tform_body
     from bosdyn.client.lease import LeaseClient, LeaseKeepAlive, LeaseWallet
    +from bosdyn.client.math_helpers import Quat, SE3Pose
     from bosdyn.client.robot_command import RobotCommandClient, RobotCommandBuilder
     from bosdyn.client.robot_state import RobotStateClient
     import bosdyn.client.util
    @@ -33,6 +34,7 @@
     
     import graph_nav_util
     
    +
     class GraphNavInterface(object):
         """GraphNav service command line interface."""
     
    @@ -88,7 +90,8 @@ def __init__(self, robot, upload_path):
                 '5': self._upload_graph_and_snapshots,
                 '6': self._navigate_to,
                 '7': self._navigate_route,
    -            '8': self._clear_graph
    +            '8': self._navigate_to_anchor,
    +            '9': self._clear_graph
             }
     
         def _get_localization_state(self, *args):
    @@ -132,8 +135,8 @@ def _set_initial_localization_waypoint(self, *args):
             self._graph_nav_client.set_localization(
                 initial_guess_localization=localization,
                 # It's hard to get the pose perfect, search +/-20 deg and +/-20cm (0.2m).
    -            max_distance = 0.2,
    -            max_yaw = 20.0 * math.pi / 180.0,
    +            max_distance=0.2,
    +            max_yaw=20.0 * math.pi / 180.0,
                 fiducial_init=graph_nav_pb2.SetLocalizationRequest.FIDUCIAL_INIT_NO_FIDUCIAL,
                 ko_tform_body=current_odom_tform_body)
     
    @@ -153,7 +156,6 @@ def _list_graph_waypoint_and_edge_ids(self, *args):
             self._current_annotation_name_to_wp_id, self._current_edges = graph_nav_util.update_waypoints_and_edges(
                 graph, localization_id)
     
    -
         def _upload_graph_and_snapshots(self, *args):
             """Upload the graph and snapshots to the robot."""
             print("Loading the graph from disk into local storage...")
    @@ -172,6 +174,8 @@ def _upload_graph_and_snapshots(self, *args):
                     waypoint_snapshot.ParseFromString(snapshot_file.read())
                     self._current_waypoint_snapshots[waypoint_snapshot.id] = waypoint_snapshot
             for edge in self._current_graph.edges:
    +            if len(edge.snapshot_id) == 0:
    +                continue
                 # Load the edge snapshots from disk.
                 with open(self._upload_filepath + "/edge_snapshots/{}".format(edge.snapshot_id),
                           "rb") as snapshot_file:
    @@ -180,8 +184,10 @@ def _upload_graph_and_snapshots(self, *args):
                     self._current_edge_snapshots[edge_snapshot.id] = edge_snapshot
             # Upload the graph to the robot.
             print("Uploading the graph and snapshots to the robot...")
    +        true_if_empty = not len(self._current_graph.anchoring.anchors)
             response = self._graph_nav_client.upload_graph(lease=self._lease.lease_proto,
    -                                                       graph=self._current_graph)
    +                                                       graph=self._current_graph,
    +                                                       generate_new_anchoring=true_if_empty)
             # Upload the snapshots to the robot.
             for snapshot_id in response.unknown_waypoint_snapshot_ids:
                 waypoint_snapshot = self._current_waypoint_snapshots[snapshot_id]
    @@ -202,6 +208,72 @@ def _upload_graph_and_snapshots(self, *args):
                 print("Upload complete! The robot is currently not localized to the map; please localize", \
                        "the robot using commands (2) or (3) before attempting a navigation command.")
     
    +    def _navigate_to_anchor(self, *args):
    +        """Navigate to a pose in seed frame, using anchors."""
    +        # The following options are accepted for arguments: [x, y], [x, y, yaw], [x, y, z, yaw],
    +        # [x, y, z, qw, qx, qy, qz].
    +        # When a value for z is not specified, we use the current z height.
    +        # When only yaw is specified, the quaternion is constructed from the yaw.
    +        # When yaw is not specified, an identity quaternion is used.
    +
    +        if len(args) < 1 or len(args[0]) not in [2, 3, 4, 7]:
    +            print("Invalid arguments supplied.")
    +            return
    +
    +        seed_T_goal = SE3Pose(float(args[0][0]), float(args[0][1]), 0.0, Quat())
    +
    +        if len(args[0]) in [4, 7]:
    +            seed_T_goal.z = float(args[0][2])
    +        else:
    +            localization_state = self._graph_nav_client.get_localization_state()
    +            if not localization_state.localization.waypoint_id:
    +                print("Robot not localized")
    +                return
    +            seed_T_goal.z = localization_state.localization.seed_tform_body.position.z
    +
    +        if len(args[0]) == 3:
    +            seed_T_goal.rot = Quat.from_yaw(float(args[0][2]))
    +        elif len(args[0]) == 4:
    +            seed_T_goal.rot = Quat.from_yaw(float(args[0][3]))
    +        elif len(args[0]) == 7:
    +            seed_T_goal.rot = Quat(w=float(args[0][3]), x=float(args[0][4]), y=float(args[0][5]),
    +                                   z=float(args[0][6]))
    +
    +        self._lease = self._lease_wallet.get_lease()
    +        if not self.toggle_power(should_power_on=True):
    +            print("Failed to power on the robot, and cannot complete navigate to request.")
    +            return
    +
    +        # Stop the lease keepalive and create a new sublease for graph nav.
    +        self._lease = self._lease_wallet.advance()
    +        sublease = self._lease.create_sublease()
    +        self._lease_keepalive.shutdown()
    +        nav_to_cmd_id = None
    +        # Navigate to the destination.
    +        is_finished = False
    +        while not is_finished:
    +            # Issue the navigation command about twice a second such that it is easy to terminate the
    +            # navigation command (with estop or killing the program).
    +            try:
    +                nav_to_cmd_id = self._graph_nav_client.navigate_to_anchor(
    +                    seed_T_goal.to_proto(), 1.0, leases=[sublease.lease_proto],
    +                    command_id=nav_to_cmd_id)
    +            except ResponseError as e:
    +                print("Error while navigating {}".format(e))
    +                break
    +            time.sleep(.5)  # Sleep for half a second to allow for command execution.
    +            # Poll the robot for feedback to determine if the navigation command is complete. Then sit
    +            # the robot down once it is finished.
    +            is_finished = self._check_success(nav_to_cmd_id)
    +
    +        self._lease = self._lease_wallet.advance()
    +        self._lease_keepalive = LeaseKeepAlive(self._lease_client)
    +
    +        # Update the lease and power off the robot if appropriate.
    +        if self._powered_on and not self._started_powered_on:
    +            # Sit the robot down + power off after the navigation command is complete.
    +            self.toggle_power(should_power_on=False)
    +
         def _navigate_to(self, *args):
             """Navigate to a specific waypoint."""
             # Take the first argument as the destination waypoint.
    @@ -327,7 +399,8 @@ def toggle_power(self, should_power_on):
                 motors_on = False
                 while not motors_on:
                     future = self._robot_state_client.get_robot_state_async()
    -                state_response = future.result(timeout=10) # 10 second timeout for waiting for the state response.
    +                state_response = future.result(
    +                    timeout=10)  # 10 second timeout for waiting for the state response.
                     if state_response.power_state.motor_power_state == robot_state_pb2.PowerState.STATE_ON:
                         motors_on = True
                     else:
    @@ -410,7 +483,12 @@ def run(self):
                 (5) Upload the graph and its snapshots.
                 (6) Navigate to. The destination waypoint id is the second argument.
                 (7) Navigate route. The (in-order) waypoint ids of the route are the arguments.
    -            (8) Clear the current graph.
    +            (8) Navigate to in seed frame. The following options are accepted for arguments: [x, y],
    +                [x, y, yaw], [x, y, z, yaw], [x, y, z, qw, qx, qy, qz]. (Don't type the braces).
    +                When a value for z is not specified, we use the current z height.
    +                When only yaw is specified, the quaternion is constructed from the yaw.
    +                When yaw is not specified, an identity quaternion is used.
    +            (9) Clear the current graph.
                 (q) Exit.
                 """)
                 try:
    diff --git a/python/examples/graph_nav_command_line/graph_nav_util.py b/python/examples/graph_nav_command_line/graph_nav_util.py
    index d7103cafa..92862bc07 100644
    --- a/python/examples/graph_nav_command_line/graph_nav_util.py
    +++ b/python/examples/graph_nav_command_line/graph_nav_util.py
    @@ -20,9 +20,9 @@ def pretty_print_waypoints(waypoint_id, waypoint_name, short_code_to_count, loca
         if short_code is None or short_code_to_count[short_code] != 1:
             short_code = '  '  # If the short code is not valid/unique, don't show it.
     
    -    print("%s Waypoint name: %s id: %s short code: %s" %
    -            ('->' if localization_id == waypoint_id else '  ',
    -            waypoint_name, waypoint_id, short_code))
    +    print(
    +        "%s Waypoint name: %s id: %s short code: %s" %
    +        ('->' if localization_id == waypoint_id else '  ', waypoint_name, waypoint_id, short_code))
     
     
     def find_unique_waypoint_id(short_code, graph, name_to_id):
    @@ -51,7 +51,7 @@ def find_unique_waypoint_id(short_code, graph, name_to_id):
         return ret
     
     
    -def update_waypoints_and_edges(graph, localization_id):
    +def update_waypoints_and_edges(graph, localization_id, do_print=True):
         """Update and print waypoint ids and edge ids."""
         name_to_id = dict()
         edges = dict()
    @@ -67,9 +67,7 @@ def update_waypoints_and_edges(graph, localization_id):
                 # Must be operating on an older graph nav map, since the creation_time is not
                 # available within the waypoint annotations message.
                 pass
    -        waypoint_to_timestamp.append((waypoint.id,
    -                                        timestamp,
    -                                        waypoint.annotations.name))
    +        waypoint_to_timestamp.append((waypoint.id, timestamp, waypoint.annotations.name))
     
             # Determine how many waypoints have the same short code.
             short_code = id_to_short_code(waypoint.id)
    @@ -91,13 +89,14 @@ def update_waypoints_and_edges(graph, localization_id):
     
         # Sort the set of waypoints by their creation timestamp. If the creation timestamp is unavailable,
         # fallback to sorting by annotation name.
    -    waypoint_to_timestamp = sorted(waypoint_to_timestamp, key= lambda x:(x[1], x[2]))
    +    waypoint_to_timestamp = sorted(waypoint_to_timestamp, key=lambda x: (x[1], x[2]))
     
         # Print out the waypoints name, id, and short code in a ordered sorted by the timestamp from
         # when the waypoint was created.
    -    print('%d waypoints:' % len(graph.waypoints))
    -    for waypoint in waypoint_to_timestamp:
    -        pretty_print_waypoints(waypoint[0], waypoint[2], short_code_to_count, localization_id)
    +    if do_print:
    +        print('%d waypoints:' % len(graph.waypoints))
    +        for waypoint in waypoint_to_timestamp:
    +            pretty_print_waypoints(waypoint[0], waypoint[2], short_code_to_count, localization_id)
     
         for edge in graph.edges:
             if edge.id.to_waypoint in edges:
    @@ -105,11 +104,13 @@ def update_waypoints_and_edges(graph, localization_id):
                     edges[edge.id.to_waypoint].append(edge.id.from_waypoint)
             else:
                 edges[edge.id.to_waypoint] = [edge.id.from_waypoint]
    -        print("(Edge) from waypoint {} to waypoint {} (cost {})".format(
    -            edge.id.from_waypoint, edge.id.to_waypoint, edge.annotations.cost.value))
    +        if do_print:
    +            print("(Edge) from waypoint {} to waypoint {} (cost {})".format(
    +                edge.id.from_waypoint, edge.id.to_waypoint, edge.annotations.cost.value))
     
         return name_to_id, edges
     
    +
     def sort_waypoints_chrono(graph):
         """Sort waypoints by time created."""
         waypoint_to_timestamp = []
    @@ -122,12 +123,10 @@ def sort_waypoints_chrono(graph):
                 # Must be operating on an older graph nav map, since the creation_time is not
                 # available within the waypoint annotations message.
                 pass
    -        waypoint_to_timestamp.append((waypoint.id,
    -                                        timestamp,
    -                                        waypoint.annotations.name))
    +        waypoint_to_timestamp.append((waypoint.id, timestamp, waypoint.annotations.name))
     
         # Sort the set of waypoints by their creation timestamp. If the creation timestamp is unavailable,
         # fallback to sorting by annotation name.
    -    waypoint_to_timestamp = sorted(waypoint_to_timestamp, key= lambda x:(x[1], x[2]))
    +    waypoint_to_timestamp = sorted(waypoint_to_timestamp, key=lambda x: (x[1], x[2]))
     
         return waypoint_to_timestamp
    diff --git a/python/examples/graph_nav_command_line/recording_command_line.py b/python/examples/graph_nav_command_line/recording_command_line.py
    index c106ac6dc..92b420b78 100644
    --- a/python/examples/graph_nav_command_line/recording_command_line.py
    +++ b/python/examples/graph_nav_command_line/recording_command_line.py
    @@ -12,18 +12,21 @@
     import sys
     import time
     
    -from bosdyn.api.graph_nav import map_pb2, recording_pb2
    +from bosdyn.api.graph_nav import map_pb2, map_processing_pb2, recording_pb2
     from bosdyn.client import create_standard_sdk, ResponseError, RpcError
     import bosdyn.client.channel
     from bosdyn.client.graph_nav import GraphNavClient
     from bosdyn.client.lease import LeaseClient, LeaseKeepAlive, LeaseWallet
     from bosdyn.client.math_helpers import SE3Pose, Quat
     from bosdyn.client.recording import GraphNavRecordingServiceClient
    +from bosdyn.client.map_processing import MapProcessingServiceClient
     import bosdyn.client.util
     import google.protobuf.timestamp_pb2
    +from google.protobuf import wrappers_pb2 as wrappers
     
     import graph_nav_util
     
    +
     class RecordingInterface(object):
         """Recording service command line interface."""
     
    @@ -46,6 +49,9 @@ def __init__(self, robot, download_filepath):
             # Setup the graph nav service client.
             self._graph_nav_client = robot.ensure_client(GraphNavClient.default_service_name)
     
    +        self._map_processing_client = robot.ensure_client(
    +            MapProcessingServiceClient.default_service_name)
    +
             # Store the most recent knowledge of the state of the robot based on rpc calls.
             self._current_graph = None
             self._current_edges = dict()  #maps to_waypoint to list(from_waypoint)
    @@ -63,7 +69,9 @@ def __init__(self, robot, download_filepath):
                 '5': self._download_full_graph,
                 '6': self._list_graph_waypoint_and_edge_ids,
                 '7': self._create_new_edge,
    -            '8': self._create_loop
    +            '8': self._create_loop,
    +            '9': self._auto_close_loops_prompt,
    +            'a': self._optimize_anchoring
             }
     
         def should_we_start_recording(self):
    @@ -102,7 +110,7 @@ def _start_recording(self, *args):
                 status = self._recording_client.start_recording()
                 print("Successfully started recording a map.")
             except Exception as err:
    -            print("Start recording failed: "+str(err))
    +            print("Start recording failed: " + str(err))
     
         def _stop_recording(self, *args):
             """Stop or pause recording a map."""
    @@ -110,7 +118,7 @@ def _stop_recording(self, *args):
                 status = self._recording_client.stop_recording()
                 print("Successfully stopped recording a map.")
             except Exception as err:
    -            print("Stop recording failed: "+str(err))
    +            print("Stop recording failed: " + str(err))
     
         def _get_recording_status(self, *args):
             """Get the recording service's status."""
    @@ -150,6 +158,8 @@ def _download_and_write_waypoint_snapshots(self, waypoints):
             """Download the waypoint snapshots from robot to the specified, local filepath location."""
             num_waypoint_snapshots_downloaded = 0
             for waypoint in waypoints:
    +            if len(waypoint.snapshot_id) == 0:
    +                continue
                 try:
                     waypoint_snapshot = self._graph_nav_client.download_waypoint_snapshot(
                         waypoint.snapshot_id)
    @@ -166,7 +176,11 @@ def _download_and_write_waypoint_snapshots(self, waypoints):
         def _download_and_write_edge_snapshots(self, edges):
             """Download the edge snapshots from robot to the specified, local filepath location."""
             num_edge_snapshots_downloaded = 0
    +        num_to_download = 0
             for edge in edges:
    +            if len(edge.snapshot_id) == 0:
    +                continue
    +            num_to_download += 1
                 try:
                     edge_snapshot = self._graph_nav_client.download_edge_snapshot(edge.snapshot_id)
                 except Exception:
    @@ -177,7 +191,7 @@ def _download_and_write_edge_snapshots(self, edges):
                                   edge_snapshot.SerializeToString())
                 num_edge_snapshots_downloaded += 1
                 print("Downloaded {} of the total {} edge snapshots.".format(
    -                num_edge_snapshots_downloaded, len(edges)))
    +                num_edge_snapshots_downloaded, num_to_download))
     
         def _write_bytes(self, filepath, filename, data):
             """Write data to a file."""
    @@ -186,9 +200,7 @@ def _write_bytes(self, filepath, filename, data):
                 f.write(data)
                 f.close()
     
    -    def _list_graph_waypoint_and_edge_ids(self, *args):
    -        """List the waypoint ids and edge ids of the graph currently on the robot."""
    -
    +    def _update_graph_waypoint_and_edge_ids(self, do_print=False):
             # Download current graph
             graph = self._graph_nav_client.download_graph()
             if graph is None:
    @@ -200,7 +212,11 @@ def _list_graph_waypoint_and_edge_ids(self, *args):
     
             # Update and print waypoints and edges
             self._current_annotation_name_to_wp_id, self._current_edges = graph_nav_util.update_waypoints_and_edges(
    -            graph, localization_id)
    +            graph, localization_id, do_print)
    +
    +    def _list_graph_waypoint_and_edge_ids(self, *args):
    +        """List the waypoint ids and edge ids of the graph currently on the robot."""
    +        self._update_graph_waypoint_and_edge_ids(do_print=True)
     
         def _create_new_edge(self, *args):
             """Create new edge between existing waypoints in map."""
    @@ -209,8 +225,7 @@ def _create_new_edge(self, *args):
                 print("ERROR: Specify the two waypoints to connect (short code or annotation).")
                 return
     
    -        if self._current_graph is None:
    -            self._current_graph = self._graph_nav_client.download_graph()
    +        self._update_graph_waypoint_and_edge_ids(do_print=False)
     
             from_id = graph_nav_util.find_unique_waypoint_id(args[0][0], self._current_graph,
                                                              self._current_annotation_name_to_wp_id)
    @@ -244,8 +259,7 @@ def _create_new_edge(self, *args):
         def _create_loop(self, *args):
             """Create edge from last waypoint to first waypoint."""
     
    -        if self._current_graph is None:
    -            self._current_graph = self._graph_nav_client.download_graph()
    +        self._update_graph_waypoint_and_edge_ids(do_print=False)
     
             if len(self._current_graph.waypoints) < 2:
                 self._add_message(
    @@ -258,8 +272,56 @@ def _create_loop(self, *args):
     
             self._create_new_edge(edge_waypoints)
     
    +    def _auto_close_loops_prompt(self, *args):
    +        print("""
    +        Options:
    +        (0) Close all loops.
    +        (1) Close only fiducial-based loops.
    +        (2) Close only odometry-based loops.
    +        (q) Back.
    +        """)
    +        try:
    +            inputs = input('>')
    +        except NameError:
    +            return
    +        req_type = str.split(inputs)[0]
    +        close_fiducial_loops = False
    +        close_odometry_loops = False
    +        if req_type == '0':
    +            close_fiducial_loops = True
    +            close_odometry_loops = True
    +        elif req_type == '1':
    +            close_fiducial_loops = True
    +        elif req_type == '2':
    +            close_odometry_loops = True
    +        elif req_type == 'q':
    +            return
    +        else:
    +            print("Unrecognized command. Going back.")
    +            return
    +        self._auto_close_loops(close_fiducial_loops, close_odometry_loops)
    +
    +    def _auto_close_loops(self, close_fiducial_loops, close_odometry_loops, *args):
    +        """Automatically find and close all loops in the graph."""
    +        response = self._map_processing_client.process_topology(
    +            params=map_processing_pb2.ProcessTopologyRequest.Params(
    +                do_fiducial_loop_closure=wrappers.BoolValue(value=close_fiducial_loops),
    +                do_odometry_loop_closure=wrappers.BoolValue(value=close_odometry_loops)),
    +            modify_map_on_server=True)
    +        print("Created {} new edge(s).".format(len(response.new_subgraph.edges)))
    +
    +    def _optimize_anchoring(self, *args):
    +        """Call anchoring optimization on the server, producing a globally optimal reference frame for waypoints to be expressed in."""
    +        response = self._map_processing_client.process_anchoring(
    +            params=map_processing_pb2.ProcessAnchoringRequest.Params(),
    +            modify_anchoring_on_server=True, stream_intermediate_results=False)
    +        if response.status == map_processing_pb2.ProcessAnchoringResponse.STATUS_OK:
    +            print("Optimized anchoring after {} iteration(s).".format(response.iteration))
    +        else:
    +            print("Error optimizing {}".format(response))
    +
         def _get_waypoint(self, id):
    -        '''Get waypoint from graph (return None if waypoint not found)'''
    +        """Get waypoint from graph (return None if waypoint not found)"""
     
             if self._current_graph is None:
                 self._current_graph = self._graph_nav_client.download_graph()
    @@ -272,20 +334,23 @@ def _get_waypoint(self, id):
             return None
     
         def _get_transform(self, from_wp, to_wp):
    -        '''Get transform from from-waypoint to to-waypoint.'''
    +        """Get transform from from-waypoint to to-waypoint."""
     
             from_se3 = from_wp.waypoint_tform_ko
    -        from_tf = SE3Pose(from_se3.position.x, from_se3.position.y, from_se3.position.z,
    -            Quat(w=from_se3.rotation.w, x=from_se3.rotation.x, y=from_se3.rotation.y, z=from_se3.rotation.z))
    +        from_tf = SE3Pose(
    +            from_se3.position.x, from_se3.position.y, from_se3.position.z,
    +            Quat(w=from_se3.rotation.w, x=from_se3.rotation.x, y=from_se3.rotation.y,
    +                 z=from_se3.rotation.z))
     
             to_se3 = to_wp.waypoint_tform_ko
    -        to_tf = SE3Pose(to_se3.position.x, to_se3.position.y, to_se3.position.z,
    -            Quat(w=to_se3.rotation.w, x=to_se3.rotation.x, y=to_se3.rotation.y, z=to_se3.rotation.z))
    +        to_tf = SE3Pose(
    +            to_se3.position.x, to_se3.position.y, to_se3.position.z,
    +            Quat(w=to_se3.rotation.w, x=to_se3.rotation.x, y=to_se3.rotation.y,
    +                 z=to_se3.rotation.z))
     
             from_T_to = from_tf.mult(to_tf.inverse())
             return from_T_to.to_proto()
     
    -
         def run(self):
             """Main loop for the command line interface."""
             while True:
    @@ -300,6 +365,8 @@ def run(self):
                 (6) List the waypoint ids and edge ids of the map on the robot.
                 (7) Create new edge between existing waypoints using odometry.
                 (8) Create new edge from last waypoint to first waypoint using odometry.
    +            (9) Automatically find and close loops.
    +            (a) Optimize the map's anchoring.
                 (q) Exit.
                 """)
                 try:
    diff --git a/python/examples/graph_nav_command_line/requirements.txt b/python/examples/graph_nav_command_line/requirements.txt
    index b55ec2f63..27e4dca1f 100644
    --- a/python/examples/graph_nav_command_line/requirements.txt
    +++ b/python/examples/graph_nav_command_line/requirements.txt
    @@ -1,3 +1,3 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.0
    +bosdyn-client >= 2.0
    diff --git a/python/examples/graph_nav_extract_point_cloud/README.md b/python/examples/graph_nav_extract_point_cloud/README.md
    new file mode 100644
    index 000000000..7c1a1f37b
    --- /dev/null
    +++ b/python/examples/graph_nav_extract_point_cloud/README.md
    @@ -0,0 +1,52 @@
    +
    +
    +# GraphNav Point Cloud Extractor
    +
    +This is an example program for opening and parsing a GraphNav map and extracting a globally consistent point cloud from it. This requires the map to have been run through Anchoring Optimization first (see the graph_nav_anchoring_optimization example. Note that if the map was recorded using Autowalk, it will automatically meet the prerequisites). The data is expected to be in XYZ32F format.
    +
    +## Setup Dependencies
    +
    +This example requires  Numpy, and requires python 3. Using pip, these dependencies can be installed using:
    +
    +```
    +python3 -m pip install -r requirements.txt
    +```
    +
    +## Running the Example
    +1. Record a map using AutoWalk or the Command Line interface. (If using Autowalk, transfer the map from Documents/bosdyn/autowalk/your_map.walk to your local machine using a USB cable). The map should be a directory of the form:
    +
    +````
    +- /your_map.walk
    +    + graph
    +    - waypoint_snapshots
    +    - edge_snapshots
    +````
    +
    +2. Run the point cloud extractor
    +```
    +python3 -m extract_point_cloud --path  --output 
    +```
    +
    +## Understanding the Point Cloud Extractor
    +
    +This example
    +
    +1. Loads a Graph Nav Map from a directory.
    +2. Extracts the data from each waypoint.
    +3. Transforms the data into the seed frame using the anchoring of each waypoint.
    +4. Saves the data to a .PLY file.
    +
    +
    +To view the data, use a 3D model viewer (such as MeshLab or CloudCompare).
    +
    +## What is actually in the point cloud?
    +
    +For base platform robots, the data in the point cloud will be visual feature data. It will be sparse, and correspond to 3D points with high contrast near the robot. For robots with LIDAR, the data will be a combination of visual feature data and LIDAR data. LIDAR data is downsampled to a 10cm resolution, and some horizontal surfaces (e.g the floor or ceiling) are intentionally filtered out.
    +
    +These data are used for localization purposes and shouldn't be used as, for example, a "digital twin".
    diff --git a/python/examples/graph_nav_extract_point_cloud/extract_point_cloud.py b/python/examples/graph_nav_extract_point_cloud/extract_point_cloud.py
    new file mode 100644
    index 000000000..9d61374ce
    --- /dev/null
    +++ b/python/examples/graph_nav_extract_point_cloud/extract_point_cloud.py
    @@ -0,0 +1,155 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +import argparse
    +import numpy as np
    +import os
    +import sys
    +from bosdyn.api.graph_nav import map_pb2
    +from bosdyn.client.frame_helpers import *
    +from bosdyn.client.math_helpers import *
    +
    +"""
    +This example shows how to load a graph nav map and extract a point cloud in the seed frame.
    +This requires a map which has been globally optimized using anchoring optimization.
    +It will export a graph nav map as a PLY file. Use a mesh viewer (such as MeshLab or CloudCompare),
    +to view the output.
    +"""
    +
    +
    +def get_point_cloud_data_in_seed_frame(waypoints, snapshots, anchorings, waypoint_id):
    +    """
    +    Create a 3 x N numpy array of points in the seed frame. Note that in graph_nav, "point cloud" refers to the
    +    feature cloud of a waypoint -- that is, a collection of visual features observed by all five cameras at a particular
    +    point in time. The visual features are associated with points that are rigidly attached to a waypoint.
    +    :param waypoints: dict of waypoint ID to waypoint.
    +    :param snapshots: dict of waypoint snapshot ID to waypoint snapshot.
    +    :param anchorings: dict of waypoint ID to the anchoring of that waypoint w.r.t the map.
    +    :param waypoint_id: the waypoint ID of the waypoint whose point cloud we want to render.
    +    :return: a 3 x N numpy array in the seed frame.
    +    """
    +    wp = waypoints[waypoint_id]
    +    snapshot = snapshots[wp.snapshot_id]
    +    cloud = snapshot.point_cloud
    +    odom_tform_cloud = get_a_tform_b(cloud.source.transforms_snapshot, ODOM_FRAME_NAME,
    +                                     cloud.source.frame_name_sensor)
    +    waypoint_tform_odom = SE3Pose.from_obj(wp.waypoint_tform_ko)
    +    waypoint_tform_cloud = waypoint_tform_odom * odom_tform_cloud
    +    if waypoint_id not in anchorings:
    +        raise Exception("{} not found in anchorings. Does the map have anchoring data?".format(waypoint_id))
    +    seed_tform_cloud = SE3Pose.from_obj(anchorings[waypoint_id].seed_tform_waypoint) * waypoint_tform_cloud
    +    point_cloud_data = np.frombuffer(cloud.data, dtype=np.float32).reshape(int(cloud.num_points), 3)
    +    return seed_tform_cloud.transform_cloud(point_cloud_data)
    +
    +
    +
    +def load_map(path):
    +    """
    +    Load a map from the given file path.
    +    :param path: Path to the root directory of the map.
    +    :return: the graph, waypoints, waypoint snapshots, edge snapshots, and anchorings.
    +    """
    +    with open(os.path.join(path, "graph"), "rb") as graph_file:
    +        # Load the graph file and deserialize it. The graph file is a protobuf containing only the waypoints and the
    +        # edges between them.
    +        data = graph_file.read()
    +        current_graph = map_pb2.Graph()
    +        current_graph.ParseFromString(data)
    +
    +        # Set up maps from waypoint ID to waypoints, edges, snapshots, etc.
    +        current_waypoints = {}
    +        current_waypoint_snapshots = {}
    +        current_edge_snapshots = {}
    +        current_anchors = {}
    +        current_anchored_world_objects = {}
    +
    +        # Load the anchored world objects first so we can look in each waypoint snapshot as we load it.
    +        for anchored_world_object in current_graph.anchoring.objects:
    +            current_anchored_world_objects[anchored_world_object.id] = (anchored_world_object,)
    +        # For each waypoint, load any snapshot associated with it.
    +        for waypoint in current_graph.waypoints:
    +            current_waypoints[waypoint.id] = waypoint
    +
    +            if len(waypoint.snapshot_id) == 0:
    +                continue
    +            # Load the snapshot. Note that snapshots contain all of the raw data in a waypoint and may be large.
    +            file_name = os.path.join(path, "waypoint_snapshots", waypoint.snapshot_id)
    +            if not os.path.exists(file_name):
    +                continue
    +            with open(file_name, "rb") as snapshot_file:
    +                waypoint_snapshot = map_pb2.WaypointSnapshot()
    +                waypoint_snapshot.ParseFromString(snapshot_file.read())
    +                current_waypoint_snapshots[waypoint_snapshot.id] = waypoint_snapshot
    +
    +                for fiducial in waypoint_snapshot.objects:
    +                    if not fiducial.HasField("apriltag_properties"):
    +                        continue
    +
    +                    str_id = str(fiducial.apriltag_properties.tag_id)
    +                    if (str_id in current_anchored_world_objects and
    +                            len(current_anchored_world_objects[str_id]) == 1):
    +
    +                        # Replace the placeholder tuple with a tuple of (wo, waypoint, fiducial).
    +                        anchored_wo = current_anchored_world_objects[str_id][0]
    +                        current_anchored_world_objects[str_id] = (anchored_wo, waypoint, fiducial)
    +
    +        # Similarly, edges have snapshot data.
    +        for edge in current_graph.edges:
    +            if len(edge.snapshot_id) == 0:
    +                continue
    +            file_name = os.path.join(path, "edge_snapshots", edge.snapshot_id)
    +            if not os.path.exists(file_name):
    +                continue
    +            with open(file_name, "rb") as snapshot_file:
    +                edge_snapshot = map_pb2.EdgeSnapshot()
    +                edge_snapshot.ParseFromString(snapshot_file.read())
    +                current_edge_snapshots[edge_snapshot.id] = edge_snapshot
    +        for anchor in current_graph.anchoring.anchors:
    +            current_anchors[anchor.id] = anchor
    +        print("Loaded graph with {} waypoints, {} edges, {} anchors, and {} anchored world objects".
    +              format(len(current_graph.waypoints), len(current_graph.edges),
    +                     len(current_graph.anchoring.anchors), len(current_graph.anchoring.objects)))
    +        return (current_graph, current_waypoints, current_waypoint_snapshots,
    +                current_edge_snapshots, current_anchors, current_anchored_world_objects)
    +
    +
    +def write_ply(data, output):
    +    """
    +    Writes an ASCII PLY file to the output file path.
    +    """
    +    print('Saving to {}'.format(output))
    +    with open(output, 'w') as f:
    +        num_points = data.shape[0]
    +        f.write('ply\nformat ascii 1.0\nelement vertex {}\nproperty float x\nproperty float y\nproperty float z\nend_header\n'.format(num_points))
    +
    +        for i in range(0, num_points):
    +            (x, y, z) = data[i, :]
    +            f.write('{} {} {}\n'.format(x, y, z))
    +
    +def main(argv):
    +    parser = argparse.ArgumentParser(description=__doc__)
    +    parser.add_argument('--path', type=str, help='Map to extract.', required=True)
    +    parser.add_argument('--output', type=str, help='Output PLY file.', required=True)
    +
    +    options = parser.parse_args(argv)
    +    # Load the map from the given file.
    +    (current_graph, current_waypoints, current_waypoint_snapshots, current_edge_snapshots,
    +     current_anchors, current_anchored_world_objects) = load_map(options.path)
    +
    +    # Concatenate the data from all waypoints.
    +    data = None
    +    for wp in current_graph.waypoints:
    +        cloud_data = get_point_cloud_data_in_seed_frame(current_waypoints, current_waypoint_snapshots, current_anchors, wp.id)
    +        if data is None:
    +            data = cloud_data
    +        else:
    +            data = np.concatenate((data, cloud_data))
    +
    +    # Save to a PLY file.
    +    write_ply(data, options.output)
    +
    +if __name__ == '__main__':
    +    main(sys.argv[1:])
    diff --git a/python/examples/graph_nav_extract_point_cloud/requirements.txt b/python/examples/graph_nav_extract_point_cloud/requirements.txt
    new file mode 100644
    index 000000000..bb3effec8
    --- /dev/null
    +++ b/python/examples/graph_nav_extract_point_cloud/requirements.txt
    @@ -0,0 +1,4 @@
    +-f ../../../prebuilt
    +
    +bosdyn-client >= 3.0
    +numpy >= 1.16.4
    diff --git a/python/examples/graph_nav_view_map/requirements.txt b/python/examples/graph_nav_view_map/requirements.txt
    index 492e4ccf0..e2ac4a14f 100644
    --- a/python/examples/graph_nav_view_map/requirements.txt
    +++ b/python/examples/graph_nav_view_map/requirements.txt
    @@ -1,5 +1,5 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.0
    +bosdyn-client >= 2.0
     numpy >= 1.16.4
     vtk < 8.2
    diff --git a/python/examples/graph_nav_view_map/view_map.py b/python/examples/graph_nav_view_map/view_map.py
    index 8a0576dc7..79ec839db 100644
    --- a/python/examples/graph_nav_view_map/view_map.py
    +++ b/python/examples/graph_nav_view_map/view_map.py
    @@ -4,6 +4,7 @@
     # is subject to the terms and conditions of the Boston Dynamics Software
     # Development Kit License (20191101-BDSDK-SL).
     
    +import argparse
     from vtk.util import numpy_support
     import google.protobuf.timestamp_pb2
     import math
    @@ -236,11 +237,18 @@ def load_map(path):
             current_waypoints = {}
             current_waypoint_snapshots = {}
             current_edge_snapshots = {}
    +        current_anchors = {}
    +        current_anchored_world_objects = {}
     
    +        # Load the anchored world objects first so we can look in each waypoint snapshot as we load it.
    +        for anchored_world_object in current_graph.anchoring.objects:
    +            current_anchored_world_objects[anchored_world_object.id] = (anchored_world_object,)
             # For each waypoint, load any snapshot associated with it.
             for waypoint in current_graph.waypoints:
                 current_waypoints[waypoint.id] = waypoint
     
    +            if len(waypoint.snapshot_id) == 0:
    +                continue
                 # Load the snapshot. Note that snapshots contain all of the raw data in a waypoint and may be large.
                 file_name = os.path.join(path, "waypoint_snapshots", waypoint.snapshot_id)
                 if not os.path.exists(file_name):
    @@ -249,8 +257,23 @@ def load_map(path):
                     waypoint_snapshot = map_pb2.WaypointSnapshot()
                     waypoint_snapshot.ParseFromString(snapshot_file.read())
                     current_waypoint_snapshots[waypoint_snapshot.id] = waypoint_snapshot
    +
    +                for fiducial in waypoint_snapshot.objects:
    +                    if not fiducial.HasField("apriltag_properties"):
    +                        continue
    +
    +                    str_id = str(fiducial.apriltag_properties.tag_id)
    +                    if (str_id in current_anchored_world_objects and
    +                            len(current_anchored_world_objects[str_id]) == 1):
    +
    +                        # Replace the placeholder tuple with a tuple of (wo, waypoint, fiducial).
    +                        anchored_wo = current_anchored_world_objects[str_id][0]
    +                        current_anchored_world_objects[str_id] = (anchored_wo, waypoint, fiducial)
    +
             # Similarly, edges have snapshot data.
             for edge in current_graph.edges:
    +            if len(edge.snapshot_id) == 0:
    +                continue
                 file_name = os.path.join(path, "edge_snapshots", edge.snapshot_id)
                 if not os.path.exists(file_name):
                     continue
    @@ -258,10 +281,59 @@ def load_map(path):
                     edge_snapshot = map_pb2.EdgeSnapshot()
                     edge_snapshot.ParseFromString(snapshot_file.read())
                     current_edge_snapshots[edge_snapshot.id] = edge_snapshot
    -        print("Loaded graph with {} waypoints and {} edges".format(
    -            len(current_graph.waypoints), len(current_graph.edges)))
    +        for anchor in current_graph.anchoring.anchors:
    +            current_anchors[anchor.id] = anchor
    +        print("Loaded graph with {} waypoints, {} edges, {} anchors, and {} anchored world objects".
    +              format(len(current_graph.waypoints), len(current_graph.edges),
    +                     len(current_graph.anchoring.anchors), len(current_graph.anchoring.objects)))
             return (current_graph, current_waypoints, current_waypoint_snapshots,
    -                current_edge_snapshots)
    +                current_edge_snapshots, current_anchors, current_anchored_world_objects)
    +
    +
    +def create_anchored_graph_objects(current_graph, current_waypoint_snapshots, current_waypoints,
    +                                  current_anchors, current_anchored_world_objects, renderer):
    +    """
    +    Creates all the VTK objects associated with the graph, in seed frame, if they are anchored.
    +    :param current_graph: the graph to use.
    +    :param current_waypoint_snapshots: dict from snapshot id to snapshot.
    +    :param current_waypoints: dict from waypoint id to waypoint.
    +    :param renderer: The VTK renderer
    +    :return: the average position in world space of all the waypoints.
    +    """
    +    waypoint_objects = {}
    +    avg_pos = np.array([0.0, 0.0, 0.0])
    +    waypoints_in_anchoring = 0
    +    # Create VTK objects associated with each waypoint.
    +    for waypoint in current_graph.waypoints:
    +        if waypoint.id in current_anchors:
    +            waypoint_object = create_waypoint_object(renderer, current_waypoints,
    +                                                     current_waypoint_snapshots, waypoint.id)
    +            seed_tform_waypoint = SE3Pose.from_obj(
    +                current_anchors[waypoint.id].seed_tform_waypoint).to_matrix()
    +            waypoint_object.SetUserTransform(mat_to_vtk(seed_tform_waypoint))
    +            make_text(waypoint.annotations.name, seed_tform_waypoint[:3, 3], renderer)
    +            avg_pos += seed_tform_waypoint[:3, 3]
    +            waypoints_in_anchoring += 1
    +
    +    avg_pos /= waypoints_in_anchoring
    +
    +    # Create VTK objects associated with each edge.
    +    for edge in current_graph.edges:
    +        if edge.id.from_waypoint in current_anchors and edge.id.to_waypoint in current_anchors:
    +            seed_tform_from = SE3Pose.from_obj(
    +                current_anchors[edge.id.from_waypoint].seed_tform_waypoint).to_matrix()
    +            from_tform_to = SE3Pose.from_obj(edge.from_tform_to).to_matrix()
    +            create_edge_object(from_tform_to, seed_tform_from, renderer)
    +
    +    # Create VTK objects associated with each anchored world object.
    +    for anchored_wo in current_anchored_world_objects.values():
    +        # anchored_wo is a tuple of (anchored_world_object, waypoint, fiducial).
    +        (fiducial_object, _) = create_fiducial_object(anchored_wo[2], anchored_wo[1], renderer)
    +        seed_tform_fiducial = SE3Pose.from_obj(anchored_wo[0].seed_tform_object).to_matrix()
    +        fiducial_object.SetUserTransform(mat_to_vtk(seed_tform_fiducial))
    +        make_text(anchored_wo[0].id, seed_tform_fiducial[:3, 3], renderer)
    +
    +    return avg_pos
     
     
     def create_graph_objects(current_graph, current_waypoint_snapshots, current_waypoints, renderer):
    @@ -295,6 +367,8 @@ def create_graph_objects(current_graph, current_waypoint_snapshots, current_wayp
             curr_element = queue[0]
             queue.pop(0)
             curr_waypoint = curr_element[0]
    +        if curr_waypoint.id in visited:
    +            continue
             visited[curr_waypoint.id] = True
     
             # We now know the global pose of this waypoint, so set the pose.
    @@ -308,15 +382,13 @@ def create_graph_objects(current_graph, current_waypoint_snapshots, current_wayp
                 snapshot = current_waypoint_snapshots[curr_waypoint.snapshot_id]
                 for fiducial in snapshot.objects:
                     if fiducial.HasField("apriltag_properties"):
    -                    (fiducial_object,
    -                     curr_wp_tform_fiducial) = create_fiducial_object(fiducial, curr_waypoint,
    -                                                                      renderer)
    +                    (fiducial_object, curr_wp_tform_fiducial) = create_fiducial_object(
    +                        fiducial, curr_waypoint, renderer)
                         world_tform_fiducial = np.dot(world_tform_current_waypoint,
                                                       vtk_to_mat(curr_wp_tform_fiducial))
                         fiducial_object.SetUserTransform(mat_to_vtk(world_tform_fiducial))
    -                    make_text(
    -                        str(fiducial.apriltag_properties.tag_id), world_tform_fiducial[:3, 3],
    -                        renderer)
    +                    make_text(str(fiducial.apriltag_properties.tag_id), world_tform_fiducial[:3, 3],
    +                              renderer)
     
             # Now, for each edge, walk along the edge and concatenate the transform to the neighbor.
             for edge in current_graph.edges:
    @@ -338,24 +410,37 @@ def create_graph_objects(current_graph, current_waypoint_snapshots, current_wayp
                     # Add the neighbor to the queue.
                     queue.append((current_waypoints[edge.id.from_waypoint], world_tform_from_wp))
                     avg_pos += world_tform_from_wp[:3, 3]
    +
    +    # Compute the average waypoint position to place the camera appropriately.
    +    avg_pos /= len(current_waypoints)
         return avg_pos
     
     
     def main(argv):
    +    parser = argparse.ArgumentParser(description=__doc__)
    +    parser.add_argument('path', type=str, help='Map to draw.')
    +    parser.add_argument('-a', '--anchoring', action='store_true',
    +                        help='Draw the map according to the anchoring (in seed frame).')
    +    options = parser.parse_args(argv)
         # Load the map from the given file.
    -    path = argv[0]
    -    (current_graph, current_waypoints, current_waypoint_snapshots,
    -     current_edge_snapshots) = load_map(path)
    +    (current_graph, current_waypoints, current_waypoint_snapshots, current_edge_snapshots,
    +     current_anchors, current_anchored_world_objects) = load_map(options.path)
     
         # Create the renderer.
         renderer = vtk.vtkRenderer()
         renderer.SetBackground(0.05, 0.1, 0.15)
     
    -    avg_pos = create_graph_objects(current_graph, current_waypoint_snapshots, current_waypoints,
    -                                   renderer)
    +    if options.anchoring:
    +        if len(current_graph.anchoring.anchors) == 0:
    +            print('No anchors to draw.')
    +            sys.exit(-1)
    +        avg_pos = create_anchored_graph_objects(current_graph, current_waypoint_snapshots,
    +                                                current_waypoints, current_anchors,
    +                                                current_anchored_world_objects, renderer)
    +    else:
    +        avg_pos = create_graph_objects(current_graph, current_waypoint_snapshots, current_waypoints,
    +                                       renderer)
     
    -    # Compute the average waypoint position to place the camera appropriately.
    -    avg_pos /= len(current_waypoints)
         camera_pos = avg_pos + np.array([-1, 0, 5])
     
         camera = renderer.GetActiveCamera()
    @@ -364,7 +449,7 @@ def main(argv):
     
         # Create the VTK renderer and interactor.
         renderWindow = vtk.vtkRenderWindow()
    -    renderWindow.SetWindowName(path)
    +    renderWindow.SetWindowName(options.path)
         renderWindow.AddRenderer(renderer)
         renderWindowInteractor = vtk.vtkRenderWindowInteractor()
         renderWindowInteractor.SetRenderWindow(renderWindow)
    @@ -380,7 +465,4 @@ def main(argv):
     
     
     if __name__ == '__main__':
    -    if (len(sys.argv) != 2):
    -        print("Usage: view_map.py ")
    -        sys.exit(-1)
         main(sys.argv[1:])
    diff --git a/python/examples/hello_spot/hello_spot.py b/python/examples/hello_spot/hello_spot.py
    index 13e63fa95..f5dd253c7 100644
    --- a/python/examples/hello_spot/hello_spot.py
    +++ b/python/examples/hello_spot/hello_spot.py
    @@ -152,6 +152,7 @@ def _maybe_display_image(image, display_time=3.0):
             logger = bosdyn.client.util.get_logger()
             logger.warning("Exception thrown displaying image. %r", exc)
     
    +
     def _maybe_save_image(image, path):
         """Try to save image, if client has correct deps."""
         logger = bosdyn.client.util.get_logger()
    @@ -180,8 +181,14 @@ def main(argv):
         """Command line interface."""
         parser = argparse.ArgumentParser()
         bosdyn.client.util.add_common_arguments(parser)
    -    parser.add_argument('-s', '--save', action='store_true', help='Save the image captured by Spot to the working directory. To chose the save location, use --save_path instead.')
    -    parser.add_argument('--save-path', default=None, nargs='?', help='Save the image captured by Spot to the provided directory. Invalid path saves to working directory.')
    +    parser.add_argument(
    +        '-s', '--save', action='store_true', help=
    +        'Save the image captured by Spot to the working directory. To chose the save location, use --save_path instead.'
    +    )
    +    parser.add_argument(
    +        '--save-path', default=None, nargs='?', help=
    +        'Save the image captured by Spot to the provided directory. Invalid path saves to working directory.'
    +    )
         options = parser.parse_args(argv)
         try:
             hello_spot(options)
    diff --git a/python/examples/hello_spot/requirements.txt b/python/examples/hello_spot/requirements.txt
    index 3d994b640..c46a7b84c 100644
    --- a/python/examples/hello_spot/requirements.txt
    +++ b/python/examples/hello_spot/requirements.txt
    @@ -1,6 +1,6 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.2
    +bosdyn-client >= 2.2
     
     Pillow==6.1.0
     
    diff --git a/python/examples/logging/requirements.txt b/python/examples/logging/requirements.txt
    index ccf6f8ef8..e2e457ae8 100644
    --- a/python/examples/logging/requirements.txt
    +++ b/python/examples/logging/requirements.txt
    @@ -1,3 +1,3 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.1
    +bosdyn-client >= 2.1
    diff --git a/python/examples/mission_question_answerer/mission_question_answerer.py b/python/examples/mission_question_answerer/mission_question_answerer.py
    index 46ad88035..6f14677f7 100644
    --- a/python/examples/mission_question_answerer/mission_question_answerer.py
    +++ b/python/examples/mission_question_answerer/mission_question_answerer.py
    @@ -15,7 +15,6 @@
     import bosdyn.client.util
     from bosdyn.mission.client import MissionClient
     
    -
     LOGGER = logging.getLogger(__name__)
     
     
    diff --git a/python/examples/mission_question_answerer/requirements.txt b/python/examples/mission_question_answerer/requirements.txt
    index ce6bf0db0..327d319a1 100644
    --- a/python/examples/mission_question_answerer/requirements.txt
    +++ b/python/examples/mission_question_answerer/requirements.txt
    @@ -1,4 +1,4 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.0
    -bosdyn-mission ~= 2.0
    +bosdyn-client >= 2.0
    +bosdyn-mission >= 2.0
    diff --git a/python/examples/mission_recorder/mission_recorder.py b/python/examples/mission_recorder/mission_recorder.py
    index 83f8e573b..1aa0bb9fe 100644
    --- a/python/examples/mission_recorder/mission_recorder.py
    +++ b/python/examples/mission_recorder/mission_recorder.py
    @@ -16,9 +16,12 @@
     import signal
     import threading
     import time
    +import traceback
    +
    +from google.protobuf import wrappers_pb2 as wrappers
     
     from bosdyn.api import geometry_pb2, world_object_pb2
    -from bosdyn.api.graph_nav import graph_nav_pb2, map_pb2, recording_pb2
    +from bosdyn.api.graph_nav import graph_nav_pb2, map_pb2, map_processing_pb2, recording_pb2
     from bosdyn.api.mission import nodes_pb2
     import bosdyn.api.robot_state_pb2 as robot_state_proto
     import bosdyn.api.spot.robot_command_pb2 as spot_command_pb2
    @@ -30,6 +33,7 @@
     from bosdyn.client.estop import EstopClient, EstopEndpoint, EstopKeepAlive
     from bosdyn.client.graph_nav import GraphNavClient
     from bosdyn.client.lease import LeaseClient, LeaseKeepAlive
    +from bosdyn.client.map_processing import MapProcessingServiceClient
     from bosdyn.client.power import PowerClient
     from bosdyn.client.robot_command import RobotCommandClient, RobotCommandBuilder
     from bosdyn.client.recording import GraphNavRecordingServiceClient, NotLocalizedToEndError
    @@ -54,10 +58,9 @@
     NAV_VELOCITY_LIMITS = geometry_pb2.SE2VelocityLimit(
         max_vel=geometry_pb2.SE2Velocity(
             linear=geometry_pb2.Vec2(x=NAV_VELOCITY_MAX_X, y=NAV_VELOCITY_MAX_Y),
    -        angular=NAV_VELOCITY_MAX_YAW),
    -    min_vel=geometry_pb2.SE2Velocity(
    -        linear=geometry_pb2.Vec2(x=-NAV_VELOCITY_MAX_X, y=-NAV_VELOCITY_MAX_Y),
    -        angular=-NAV_VELOCITY_MAX_YAW))
    +        angular=NAV_VELOCITY_MAX_YAW), min_vel=geometry_pb2.SE2Velocity(
    +            linear=geometry_pb2.Vec2(x=-NAV_VELOCITY_MAX_X, y=-NAV_VELOCITY_MAX_Y),
    +            angular=-NAV_VELOCITY_MAX_YAW))
     
     
     def _grpc_or_log(desc, thunk):
    @@ -133,9 +136,6 @@ def __init__(self, robot, download_filepath):
             # Filepath for the location to put the downloaded graph and snapshots.
             self._download_filepath = download_filepath
     
    -        # List of waypoints in map
    -        self._waypoint_list = []
    -
             # List of waypoint commands
             self._waypoint_commands = []
     
    @@ -155,6 +155,7 @@ def __init__(self, robot, download_filepath):
                 self._estop_client = None
                 self._estop_endpoint = None
     
    +        self._map_processing_client = robot.ensure_client(MapProcessingServiceClient.default_service_name)
             self._power_client = robot.ensure_client(PowerClient.default_service_name)
             self._robot_state_client = robot.ensure_client(RobotStateClient.default_service_name)
             self._robot_command_client = robot.ensure_client(RobotCommandClient.default_service_name)
    @@ -167,10 +168,15 @@ def __init__(self, robot, download_filepath):
             # Setup the graph nav service client.
             self._graph_nav_client = robot.ensure_client(GraphNavClient.default_service_name)
     
    +        # Local copy of the graph.
    +        self._graph = None
    +        self._all_graph_wps_in_order = []
    +
             self._robot_state_task = AsyncRobotState(self._robot_state_client)
             self._async_tasks = AsyncTasks([self._robot_state_task])
             self._lock = threading.Lock()
             self._command_dictionary = {
    +            27: self._stop, # ESC key
                 ord('\t'): self._quit_program,
                 ord('T'): self._toggle_time_sync,
                 ord(' '): self._toggle_estop,
    @@ -181,6 +187,7 @@ def __init__(self, robot, download_filepath):
                 ord('w'): self._move_forward,
                 ord('s'): self._move_backward,
                 ord('a'): self._strafe_left,
    +            ord('c'): self._auto_close_loops,
                 ord('d'): self._strafe_right,
                 ord('q'): self._turn_left,
                 ord('e'): self._turn_right,
    @@ -309,7 +316,7 @@ def _drive_draw(self, stdscr, lease_keep_alive):
             stdscr.addstr(15, 0, "          [T]: Time-sync, [SPACE]: Estop, [P]: Power")
             stdscr.addstr(16, 0, "          [v]: Sit, [f]: Stand, [r]: Self-right     ")
             stdscr.addstr(17, 0, "          [wasd]: Directional strafing              ")
    -        stdscr.addstr(18, 0, "          [qe]: Turning                             ")
    +        stdscr.addstr(18, 0, "          [qe]: Turning, [ESC]: Stop                ")
             stdscr.addstr(19, 0, "          [m]: Start recording mission              ")
             stdscr.addstr(20, 0, "          [l]: Add fiducial localization to mission ")
             stdscr.addstr(21, 0, "          [z]: Enter desert mode                    ")
    @@ -394,6 +401,9 @@ def _turn_left(self):
         def _turn_right(self):
             self._velocity_cmd_helper('turn_right', v_rot=-VELOCITY_BASE_ANGULAR)
     
    +    def _stop(self):
    +        self._start_robot_command('stop', RobotCommandBuilder.stop_command())
    +
         def _velocity_cmd_helper(self, desc='', v_x=0.0, v_y=0.0, v_rot=0.0):
             self._start_robot_command(
                 desc, RobotCommandBuilder.synchro_velocity_command(v_x=v_x, v_y=v_y, v_rot=v_rot),
    @@ -487,11 +497,8 @@ def _waypoint_str(self):
                 self._waypoint_id = 'ERROR'
     
             if self._recording and self._waypoint_id != 'NONE' and self._waypoint_id != 'ERROR':
    -            if (self._waypoint_list == []) or (self._waypoint_id != self._waypoint_list[-1]):
    -                self._waypoint_list += [self._waypoint_id]
    +            if self._waypoint_id not in self._desert_flag:
                     self._desert_flag[self._waypoint_id] = self._desert_mode
    -            if self._waypoint_commands == []:
    -                self._waypoint_commands = [self._waypoint_id]
                 return 'Current waypoint: {} [ RECORDING ]'.format(self._waypoint_id)
     
             return 'Current waypoint: {}'.format(self._waypoint_id)
    @@ -550,8 +557,23 @@ def _start_recording(self):
                 return
     
             self.add_message("Started recording map.")
    -        self._waypoint_list = []
    +        self._graph = None
    +        self._all_graph_wps_in_order = []
    +        state = self._graph_nav_client.get_localization_state()
    +        if '' == state.localization.waypoint_id:
    +            self.add_message("No localization after start recording.")
    +            self._recording_client.stop_recording()
    +            return
             self._waypoint_commands = []
    +        # Treat this sort of like RPN / postfix, where operators follow their operands.
    +        # waypoint_id LOCALIZE means "localize to waypoint_id".
    +        # INITIALIZE takes no argument.
    +        # Implicitly, we make routes between pairs of waypoint ids as they occur.
    +        # `w0 w1 LOCALIZE w2` will give a route w0->w1, whereupon we localize,
    +        # and then a route w1->w2.
    +        self._waypoint_commands.append('INITIALIZE')
    +        # Start with the first waypoint.
    +        self._waypoint_commands.append(state.localization.waypoint_id)
             self._recording = True
     
         def _stop_recording(self):
    @@ -560,7 +582,8 @@ def _stop_recording(self):
                 return True
             self._recording = False
     
    -        self._waypoint_commands += [self._waypoint_id]
    +        if self._waypoint_id != self._waypoint_commands[-1]:
    +            self._waypoint_commands += [self._waypoint_id]
     
             try:
                 status = self._recording_client.stop_recording()
    @@ -584,6 +607,9 @@ def _relocalize(self):
             self.add_message("Adding fiducial localization to mission.")
             self._waypoint_commands += [self._waypoint_id]
             self._waypoint_commands += ['LOCALIZE']
    +
    +        # Return to waypoint (in case localization shifted to another waypoint)
    +        self._waypoint_commands += [self._waypoint_id]
             return True
     
         def _enter_desert(self):
    @@ -606,6 +632,11 @@ def _generate_mission(self):
                 self.add_message("ERROR: No mission recorded.")
                 return
     
    +        # Check for empty mission
    +        if len(self._waypoint_commands) == 0:
    +            self.add_message("ERROR: No waypoints in mission.")
    +            return
    +
             # Stop recording mission
             if not self._stop_recording():
                 self.add_message("ERROR: Error while stopping recording.")
    @@ -621,8 +652,8 @@ def _generate_mission(self):
             mission = self._make_mission()
     
             # Save mission file
    -        os.mkdir(self._download_filepath + "/missions")
    -        mission_filepath = self._download_filepath + "/missions/autogenerated"
    +        os.mkdir(os.path.join(self._download_filepath, "missions"))
    +        mission_filepath = os.path.join(self._download_filepath, "missions", "autogenerated")
             write_mission(mission, mission_filepath)
     
             # Quit program
    @@ -632,13 +663,21 @@ def _make_desert(self, waypoint):
             waypoint.annotations.scan_match_region.empty.CopyFrom(
                 map_pb2.Waypoint.Annotations.LocalizeRegion.Empty())
     
    -    def _download_full_graph(self):
    +    def _download_full_graph(self, overwrite_desert_flag=None):
             """Download the graph and snapshots from the robot."""
             graph = self._graph_nav_client.download_graph()
             if graph is None:
                 self.add_message("Failed to download the graph.")
                 return False
     
    +        if overwrite_desert_flag is not None:
    +            for wp in graph.waypoints:
    +                if wp.id in overwrite_desert_flag:
    +                    # Overwrite anything that we passed in.
    +                    self._desert_flag[wp.id] = overwrite_desert_flag[wp.id]
    +                elif wp.id not in self._desert_flag:
    +                    self._desert_flag[wp.id] = False
    +
             # Mark desert waypoints
             for waypoint in graph.waypoints:
                 if self._desert_flag[waypoint.id]:
    @@ -652,12 +691,24 @@ def _download_full_graph(self):
             # Download the waypoint and edge snapshots.
             self._download_and_write_waypoint_snapshots(graph.waypoints)
             self._download_and_write_edge_snapshots(graph.edges)
    +
    +        # Cache a list of all graph waypoints, ordered by creation time.
    +        # Because we only record one (non-branching) chain, all possible routes are contained
    +        # in ranges in this list.
    +        self._graph = graph
    +        wp_to_time = []
    +        for wp in graph.waypoints:
    +            time = wp.annotations.creation_time.seconds + wp.annotations.creation_time.nanos / 1e9
    +            wp_to_time.append((wp.id, time))
    +        # Switch inner and outer grouping and grab only the waypoint names after sorting by time.
    +        self._all_graph_wps_in_order = list(zip(*sorted(wp_to_time, key=lambda x: x[1])))[0]
    +
             return True
     
         def _write_full_graph(self, graph):
             """Download the graph from robot to the specified, local filepath location."""
             graph_bytes = graph.SerializeToString()
    -        write_bytes(self._download_filepath, '/graph', graph_bytes)
    +        write_bytes(self._download_filepath, 'graph', graph_bytes)
     
         def _download_and_write_waypoint_snapshots(self, waypoints):
             """Download the waypoint snapshots from robot to the specified, local filepath location."""
    @@ -670,8 +721,8 @@ def _download_and_write_waypoint_snapshots(self, waypoints):
                     # Failure in downloading waypoint snapshot. Continue to next snapshot.
                     self.add_message("Failed to download waypoint snapshot: " + waypoint.snapshot_id)
                     continue
    -            write_bytes(self._download_filepath + '/waypoint_snapshots', '/' + waypoint.snapshot_id,
    -                        waypoint_snapshot.SerializeToString())
    +            write_bytes(os.path.join(self._download_filepath, 'waypoint_snapshots'),
    +                        waypoint.snapshot_id, waypoint_snapshot.SerializeToString())
                 num_waypoint_snapshots_downloaded += 1
                 self.add_message("Downloaded {} of the total {} waypoint snapshots.".format(
                     num_waypoint_snapshots_downloaded, len(waypoints)))
    @@ -679,18 +730,33 @@ def _download_and_write_waypoint_snapshots(self, waypoints):
         def _download_and_write_edge_snapshots(self, edges):
             """Download the edge snapshots from robot to the specified, local filepath location."""
             num_edge_snapshots_downloaded = 0
    +        num_to_download = 0
             for edge in edges:
    +            if len(edge.snapshot_id) == 0:
    +                continue
    +            num_to_download += 1
                 try:
                     edge_snapshot = self._graph_nav_client.download_edge_snapshot(edge.snapshot_id)
                 except Exception:
                     # Failure in downloading edge snapshot. Continue to next snapshot.
                     self.add_message("Failed to download edge snapshot: " + edge.snapshot_id)
                     continue
    -            write_bytes(self._download_filepath + '/edge_snapshots', '/' + edge.snapshot_id,
    -                        edge_snapshot.SerializeToString())
    +            write_bytes(os.path.join(self._download_filepath, 'edge_snapshots'),
    +                        edge.snapshot_id, edge_snapshot.SerializeToString())
                 num_edge_snapshots_downloaded += 1
                 self.add_message("Downloaded {} of the total {} edge snapshots.".format(
    -                num_edge_snapshots_downloaded, len(edges)))
    +                num_edge_snapshots_downloaded, num_to_download))
    +
    +    def _auto_close_loops(self):
    +        """Automatically find and close all loops in the graph."""
    +        close_fiducial_loops = True
    +        close_odometry_loops = True
    +        response = self._map_processing_client.process_topology(
    +            params=map_processing_pb2.ProcessTopologyRequest.Params(
    +                do_fiducial_loop_closure=wrappers.BoolValue(value=close_fiducial_loops),
    +                do_odometry_loop_closure=wrappers.BoolValue(value=close_odometry_loops)),
    +            modify_map_on_server=True)
    +        self.add_message("Created {} new edge(s).".format(len(response.new_subgraph.edges)))
     
         def _make_mission(self):
             """ Create a mission that visits each waypoint on stored path."""
    @@ -699,13 +765,28 @@ def _make_mission(self):
     
             # Create a Sequence that visits all the waypoints.
             sequence = nodes_pb2.Sequence()
    -        sequence.children.add().CopyFrom(self._make_localize_node())
     
    -        for waypoint_id in self._waypoint_commands:
    -            if waypoint_id == 'LOCALIZE':
    -                sequence.children.add().CopyFrom(self._make_localize_node())
    +        prev_waypoint_command = None
    +        first_waypoint = True
    +        for waypoint_command in self._waypoint_commands:
    +            if waypoint_command == 'LOCALIZE':
    +                if prev_waypoint_command is None:
    +                    raise RuntimeError(f'No prev waypoint; LOCALIZE')
    +                sequence.children.add().CopyFrom(self._make_localize_node(prev_waypoint_command))
    +            elif waypoint_command == 'INITIALIZE':
    +                # Initialize to any fiducial.
    +                sequence.children.add().CopyFrom(self._make_initialize_node())
                 else:
    -                sequence.children.add().CopyFrom(self._make_goto_node(waypoint_id))
    +                if first_waypoint:
    +                    # Go to the beginning of the mission using any path. May be a no-op.
    +                    sequence.children.add().CopyFrom(self._make_goto_node(waypoint_command))
    +                else:
    +                    if prev_waypoint_command is None:
    +                        raise RuntimeError(f'No prev waypoint; route to {waypoint_command}')
    +                    sequence.children.add().CopyFrom(
    +                        self._make_go_route_node(prev_waypoint_command, waypoint_command))
    +                prev_waypoint_command = waypoint_command
    +                first_waypoint = False
     
             # Return a Node with the Sequence.
             ret = nodes_pb2.Node()
    @@ -719,24 +800,83 @@ def _make_goto_node(self, waypoint_id):
             ret.name = "goto %s" % waypoint_id
             vel_limit = NAV_VELOCITY_LIMITS
     
    -        if self._desert_flag[waypoint_id]:
    +        # We don't actually know which path we will plan. It could have many feature deserts.
    +        # So, let's just allow feature deserts.
    +        tolerance = graph_nav_pb2.TravelParams.TOLERANCE_IGNORE_POOR_FEATURE_QUALITY
    +        travel_params = graph_nav_pb2.TravelParams(velocity_limit=vel_limit,
    +                                                   feature_quality_tolerance=tolerance)
    +
    +        impl = nodes_pb2.BosdynNavigateTo(travel_params=travel_params)
    +        impl.destination_waypoint_id = waypoint_id
    +        ret.impl.Pack(impl)
    +        return ret
    +
    +    def _make_go_route_node(self, prev_waypoint_id, waypoint_id):
    +        """ Create a leaf node that will go to the waypoint along a route. """
    +        ret = nodes_pb2.Node()
    +        ret.name = "go route %s -> %s" % (prev_waypoint_id, waypoint_id)
    +        vel_limit = NAV_VELOCITY_LIMITS
    +
    +        if prev_waypoint_id not in self._all_graph_wps_in_order:
    +            raise RuntimeError(f'{prev_waypoint_id} (FROM) not in {self._all_graph_wps_in_order}')
    +        if waypoint_id not in self._all_graph_wps_in_order:
    +            raise RuntimeError(f'{waypoint_id} (TO) not in {self._all_graph_wps_in_order}')
    +        prev_wp_idx = self._all_graph_wps_in_order.index(prev_waypoint_id)
    +        wp_idx = self._all_graph_wps_in_order.index(waypoint_id)
    +
    +        impl = nodes_pb2.BosdynNavigateRoute()
    +        backwards = False
    +        if wp_idx >= prev_wp_idx:
    +            impl.route.waypoint_id[:] = self._all_graph_wps_in_order[prev_wp_idx:wp_idx + 1]
    +        else:
    +            backwards = True
    +            # Switch the indices and reverse the output order.
    +            impl.route.waypoint_id[:] = reversed(self._all_graph_wps_in_order[wp_idx:prev_wp_idx + 1])
    +        edge_ids = [e.id for e in self._graph.edges]
    +        for i in range(len(impl.route.waypoint_id) - 1):
    +            eid = map_pb2.Edge.Id()
    +            # Because we only record one (non-branching) chain, and we only create routes from
    +            # older to newer waypoints, we can just follow the time-sorted list of all waypoints.
    +            if backwards:
    +                from_i = i + 1
    +                to_i = i
    +            else:
    +                from_i = i
    +                to_i = i + 1
    +            eid.from_waypoint = impl.route.waypoint_id[from_i]
    +            eid.to_waypoint = impl.route.waypoint_id[to_i]
    +            impl.route.edge_id.extend([eid])
    +            if eid not in edge_ids:
    +                raise RuntimeError(f'Was map recorded in linear chain? {eid} not in graph')
    +
    +        if any(self._desert_flag[wp_id] for wp_id in impl.route.waypoint_id):
                 tolerance = graph_nav_pb2.TravelParams.TOLERANCE_IGNORE_POOR_FEATURE_QUALITY
             else:
                 tolerance = graph_nav_pb2.TravelParams.TOLERANCE_DEFAULT
     
             travel_params = graph_nav_pb2.TravelParams(velocity_limit=vel_limit,
                                                        feature_quality_tolerance=tolerance)
    -
    -        impl = nodes_pb2.BosdynNavigateTo(travel_params=travel_params)
    -        impl.destination_waypoint_id = waypoint_id
    +        impl.travel_params.CopyFrom(travel_params)
             ret.impl.Pack(impl)
             return ret
     
    -    def _make_localize_node(self):
    +    def _make_localize_node(self, waypoint_id):
             """Make localization node."""
             loc = nodes_pb2.Node()
             loc.name = "localize robot"
     
    +        impl = nodes_pb2.BosdynGraphNavLocalize()
    +        impl.localization_request.fiducial_init = graph_nav_pb2.SetLocalizationRequest.FIDUCIAL_INIT_NEAREST_AT_TARGET
    +        impl.localization_request.initial_guess.waypoint_id = waypoint_id
    +
    +        loc.impl.Pack(impl)
    +        return loc
    +
    +    def _make_initialize_node(self):
    +        """Make initialization node."""
    +        loc = nodes_pb2.Node()
    +        loc.name = "initialize robot"
    +
             impl = nodes_pb2.BosdynGraphNavLocalize()
             impl.localization_request.fiducial_init = graph_nav_pb2.SetLocalizationRequest.FIDUCIAL_INIT_NEAREST
     
    @@ -747,7 +887,7 @@ def _make_localize_node(self):
     def write_bytes(filepath, filename, data):
         """Write data to a file."""
         os.makedirs(filepath, exist_ok=True)
    -    with open(filepath + filename, 'wb+') as f:
    +    with open(os.path.join(filepath, filename), 'wb+') as f:
             f.write(data)
             f.close()
     
    @@ -794,9 +934,15 @@ def main():
         parser.add_argument('--time-sync-interval-sec',
                             help='The interval (seconds) that time-sync estimate should be updated.',
                             type=float)
    +    parser.add_argument('--waypoint-commands-only', nargs='+',
    +                        help='Build a mission from the graph on-robot, specifying these commands')
     
         options = parser.parse_args()
     
    +    if os.path.exists(options.directory):
    +        LOGGER.error("ERROR: Directory %s already exists." % options.directory)
    +        return False
    +
         stream_handler = setup_logging(options.verbose)
     
         # Create robot object.
    @@ -810,6 +956,25 @@ def main():
             return False
     
         recorder_interface = RecorderInterface(robot, options.directory)
    +
    +    if options.waypoint_commands_only is not None:
    +        recorder_interface._waypoint_commands = options.waypoint_commands_only
    +        # Save graph map
    +        os.mkdir(recorder_interface._download_filepath)
    +        if not recorder_interface._download_full_graph(overwrite_desert_flag=[]):
    +            recorder_interface.add_message("ERROR: Error downloading graph.")
    +            return
    +
    +        LOGGER.info(recorder_interface._all_graph_wps_in_order)
    +        # Generate mission
    +        mission = recorder_interface._make_mission()
    +
    +        # Save mission file
    +        os.mkdir(recorder_interface._download_filepath + "/missions")
    +        mission_filepath = recorder_interface._download_filepath + "/missions/autogenerated"
    +        write_mission(mission, mission_filepath)
    +        return True
    +
         try:
             recorder_interface.start()
         except (ResponseError, RpcError) as err:
    @@ -817,10 +982,13 @@ def main():
             return False
     
         try:
    +        # Prevent curses from introducing a 1 second delay for ESC key
    +        os.environ.setdefault('ESCDELAY', '0')
             # Run recorder interface in curses mode, then restore terminal config.
             curses.wrapper(recorder_interface.drive)
         except Exception as e:
             LOGGER.error("Mission recorder has thrown an error: %s" % repr(e))
    +        LOGGER.error(traceback.format_exc())
         finally:
             # Restore stream handler after curses mode.
             LOGGER.addHandler(stream_handler)
    diff --git a/python/examples/mission_recorder/requirements.txt b/python/examples/mission_recorder/requirements.txt
    index 9dcff1aa1..e9c3cb3c9 100644
    --- a/python/examples/mission_recorder/requirements.txt
    +++ b/python/examples/mission_recorder/requirements.txt
    @@ -1,4 +1,4 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.1
    +bosdyn-client >= 2.1
     windows-curses==2.1.0; sys_platform == 'win32'
    diff --git a/python/examples/network_compute_bridge/Dockerfile.client b/python/examples/network_compute_bridge/Dockerfile.client
    new file mode 100644
    index 000000000..2dabff942
    --- /dev/null
    +++ b/python/examples/network_compute_bridge/Dockerfile.client
    @@ -0,0 +1,13 @@
    +FROM python:3.7-slim
    +
    +# Needed for OpenCV
    +RUN apt-get update && apt-get install ffmpeg libsm6 libxext6 -y
    +
    +COPY docker-requirements_client.txt .
    +
    +RUN python3 -m pip install -r docker-requirements_client.txt
    +
    +COPY . /app
    +WORKDIR /app
    +
    +ENTRYPOINT ["python3", "/app/identify_object.py"]
    diff --git a/python/examples/network_compute_bridge/Dockerfile.client_no_robot b/python/examples/network_compute_bridge/Dockerfile.client_no_robot
    new file mode 100644
    index 000000000..bd6cb0f42
    --- /dev/null
    +++ b/python/examples/network_compute_bridge/Dockerfile.client_no_robot
    @@ -0,0 +1,13 @@
    +FROM python:3.7-slim
    +
    +# Needed for OpenCV
    +RUN apt-get update && apt-get install ffmpeg libsm6 libxext6 -y
    +
    +COPY docker-requirements_client.txt .
    +
    +RUN python3 -m pip install -r docker-requirements_client.txt
    +
    +COPY . /app
    +WORKDIR /app
    +
    +ENTRYPOINT ["python3", "/app/identify_object_without_robot.py"]
    diff --git a/python/examples/network_compute_bridge/Dockerfile.server_cpu b/python/examples/network_compute_bridge/Dockerfile.server_cpu
    new file mode 100644
    index 000000000..864f101af
    --- /dev/null
    +++ b/python/examples/network_compute_bridge/Dockerfile.server_cpu
    @@ -0,0 +1,13 @@
    +FROM tensorflow/tensorflow:2.0.4-py3
    +
    +# Needed for OpenCV
    +RUN apt-get update && apt-get install ffmpeg libsm6 libxext6 -y
    +
    +COPY docker-requirements_server_cpu.txt .
    +
    +RUN python3 -m pip install -r docker-requirements_server_cpu.txt
    +
    +COPY . /app
    +WORKDIR /app
    +
    +ENTRYPOINT ["python3", "/app/tensorflow_server.py"]
    diff --git a/python/examples/network_compute_bridge/Dockerfile.server_gpu b/python/examples/network_compute_bridge/Dockerfile.server_gpu
    new file mode 100644
    index 000000000..f82c89577
    --- /dev/null
    +++ b/python/examples/network_compute_bridge/Dockerfile.server_gpu
    @@ -0,0 +1,13 @@
    +FROM tensorflow/tensorflow:2.0.4-gpu-py3
    +
    +# Needed for OpenCV
    +RUN apt-get update && apt-get install ffmpeg libsm6 libxext6 -y
    +
    +COPY docker-requirements_server_gpu.txt .
    +
    +RUN python3 -m pip install -r docker-requirements_server_gpu.txt
    +
    +COPY . /app
    +WORKDIR /app
    +
    +ENTRYPOINT ["python3", "/app/tensorflow_server.py"]
    diff --git a/python/examples/network_compute_bridge/README.md b/python/examples/network_compute_bridge/README.md
    index db9fd8ee5..bba212f76 100644
    --- a/python/examples/network_compute_bridge/README.md
    +++ b/python/examples/network_compute_bridge/README.md
    @@ -22,6 +22,7 @@ Examples:
         - Robot takes image, rotates it to align with the horizon and sends it to a server running `tensorflow_server.py`.
         - Results are returned to the client.  If depth data is available inside the bounding box, the robot adds depth to the result.
     
    +- [Fire Extinguisher Server](./fire_extinguisher_server/README.md): Example of a network compute bridge server for detecting fire extinguishers that uses Keras Retinanet instead of TensorFlow.
     
     ## System Diagram
     
    @@ -38,11 +39,11 @@ dependencies via:
     ```
     For non-GPU installations:
     
    -python3 -m pip install -r requirements_tensorflow_server_cpu.txt
    +python3 -m pip install -r requirements_server_cpu.txt
     
     For CUDA / NVIDIA GPU installations:
     
    -python3 -m pip install -r requirements_tensorflow_server_gpu.txt
    +python3 -m pip install -r requirements_server_gpu.txt
     ```
     
     Installation of NVIDIA drivers and CUDA is outside the scope of the document.  There are
    @@ -54,7 +55,7 @@ many tutorials available such as [this one](https://www.pyimagesearch.com/2019/0
     The client example (`identify_object.py`) does not require TensorFlow:
     
     ```
    -python3 -m pip install -r requirements_client_only.txt
    +python3 -m pip install -r requirements_client.txt
     ```
     
     
    @@ -95,6 +96,49 @@ python3 identify_object.py --username  --password  --service
     
     Note, the `USERNAME` and `PASSWORD` are your user credentials for your Spot.
     
    +## Robot-independent Execution
    +
    +To run this example without a robot in the pipeline, first launch the server with the "-r" flag:
    +
    +```
    +python3 tensorflow_server.py -r -d 
    +```
    +After launching tensorflow_server.py, the user may post requests to it using the `identify_object_without_robot.py` client example. To run this example with the above example server and model, run:
    +
    +```
    +python3 identify_object_without_robot.py --model  --server  --confidence 0.5 --model frozen_inference_graph
    +
    +For example:
    +
    +python3 identify_object_without_robot.py --confidence 0.5 --model frozen_inference_graph --input-image-dir ~/Download/images/ -v
    +```
    +
    +Example output:
    +```
    +    Got 2 objects.
    +    name: "obj1_label_person"
    +    image_properties {
    +        coordinates {
    +        vertexes {
    +            x: 1146.0
    +            y: 27.0
    +        }
    +    --- cut ---
    +```
    +
    +## Docker Execution
    +
    +This example contains the configuration files to run the python scripts described above also in Docker containers. The docker containers accept the same arguments descrined above. For example, to run the server_cpu docker container and the client docker container, follow the steps below with the correct values for the <> variables:
    +
    +```
    +sudo docker build -t ncb_server_cpu -f Dockerfile.server_cpu .
    +sudo docker run -it --network=host -v :/model_dir/ ncb_server_cpu --model-dir /model_dir/ --username  --password  
    +
    +
    +sudo docker build -t ncb_client -f Dockerfile.client .
    +sudo docker run -it --network=host ncb_client --username  --password  --service tensorflow-server --confidence 0.5 --model frozen_inference_graph --image-source frontleft_fisheye_image 
    +```
    +
     ## Troubleshooting
     
     - Ensure that your firewall is allowing traffic on the specified port (default: 50051).
    diff --git a/python/examples/network_compute_bridge/docker-requirements_client.txt b/python/examples/network_compute_bridge/docker-requirements_client.txt
    new file mode 100644
    index 000000000..acaac8dd6
    --- /dev/null
    +++ b/python/examples/network_compute_bridge/docker-requirements_client.txt
    @@ -0,0 +1,16 @@
    +bosdyn-api==3.0.0
    +bosdyn-client==3.0.0
    +bosdyn-core==3.0.0
    +certifi==2020.12.5
    +chardet==4.0.0
    +Deprecated==1.2.10
    +grpcio==1.19.0
    +idna==2.10
    +numpy==1.19.5
    +opencv-python==4.5.1.48
    +protobuf==3.7.1
    +PyJWT==2.0.1
    +requests==2.25.1
    +six==1.12.0
    +urllib3==1.26.3
    +wrapt==1.12.1
    diff --git a/python/examples/network_compute_bridge/docker-requirements_server_cpu.txt b/python/examples/network_compute_bridge/docker-requirements_server_cpu.txt
    new file mode 100644
    index 000000000..8b1c3d65d
    --- /dev/null
    +++ b/python/examples/network_compute_bridge/docker-requirements_server_cpu.txt
    @@ -0,0 +1,43 @@
    +absl-py==0.11.0
    +astor==0.8.1
    +bosdyn-api==3.0.0
    +bosdyn-client==3.0.0
    +bosdyn-core==3.0.0
    +cachetools==4.2.1
    +certifi==2020.12.5
    +chardet==4.0.0
    +Deprecated==1.2.10
    +gast==0.2.2
    +google-auth==1.27.0
    +google-auth-oauthlib==0.4.2
    +google-pasta==0.2.0
    +grpcio==1.35.0
    +h5py==2.10.0
    +idna==2.10
    +importlib-metadata==3.5.0
    +Keras-Applications==1.0.8
    +Keras-Preprocessing==1.1.2
    +Markdown==3.3.3
    +numpy==1.18.5
    +oauthlib==3.1.0
    +opencv-python==4.5.1.48
    +opt-einsum==3.3.0
    +Pillow==6.1.0
    +protobuf==3.7.1
    +pyasn1==0.4.8
    +pyasn1-modules==0.2.8
    +PyJWT==2.0.1
    +requests==2.25.1
    +requests-oauthlib==1.3.0
    +rsa==4.7.1
    +scipy==1.5.4
    +six==1.12.0
    +tensorboard==2.0.2
    +tensorflow==2.0.4
    +tensorflow-estimator==2.0.1
    +termcolor==1.1.0
    +typing-extensions==3.7.4.3
    +urllib3==1.26.3
    +Werkzeug==1.0.1
    +wrapt==1.12.1
    +zipp==3.4.0
    diff --git a/python/examples/network_compute_bridge/docker-requirements_server_gpu.txt b/python/examples/network_compute_bridge/docker-requirements_server_gpu.txt
    new file mode 100644
    index 000000000..6686474e3
    --- /dev/null
    +++ b/python/examples/network_compute_bridge/docker-requirements_server_gpu.txt
    @@ -0,0 +1,43 @@
    +absl-py==0.11.0
    +astor==0.8.1
    +bosdyn-api==3.0.0
    +bosdyn-client==3.0.0
    +bosdyn-core==3.0.0
    +cachetools==4.2.1
    +certifi==2020.12.5
    +chardet==4.0.0
    +Deprecated==1.2.10
    +gast==0.2.2
    +google-auth==1.27.0
    +google-auth-oauthlib==0.4.2
    +google-pasta==0.2.0
    +grpcio==1.35.0
    +h5py==2.10.0
    +idna==2.10
    +importlib-metadata==3.5.0
    +Keras-Applications==1.0.8
    +Keras-Preprocessing==1.1.2
    +Markdown==3.3.3
    +numpy==1.18.5
    +oauthlib==3.1.0
    +opencv-python==4.5.1.48
    +opt-einsum==3.3.0
    +Pillow==6.1.0
    +protobuf==3.7.1
    +pyasn1==0.4.8
    +pyasn1-modules==0.2.8
    +PyJWT==2.0.1
    +requests==2.25.1
    +requests-oauthlib==1.3.0
    +rsa==4.7.1
    +scipy==1.5.4
    +six==1.12.0
    +tensorboard==2.0.2
    +tensorflow-gpu==2.0.4
    +tensorflow-estimator==2.0.1
    +termcolor==1.1.0
    +typing-extensions==3.7.4.3
    +urllib3==1.26.3
    +Werkzeug==1.0.1
    +wrapt==1.12.1
    +zipp==3.4.0
    diff --git a/python/examples/network_compute_bridge/fire_extinguisher_server/Dockerfile b/python/examples/network_compute_bridge/fire_extinguisher_server/Dockerfile
    new file mode 100644
    index 000000000..f3ff869d8
    --- /dev/null
    +++ b/python/examples/network_compute_bridge/fire_extinguisher_server/Dockerfile
    @@ -0,0 +1,8 @@
    +FROM python:3.6
    +COPY . /app
    +RUN apt-get update
    +RUN apt-get install -yq --no-install-recommends libgtk2.0-dev
    +RUN python3 -m pip install numpy==1.16.2
    +RUN python3 -m pip install -r /app/requirements.txt
    +WORKDIR /app
    +ENTRYPOINT ["python3", "./retinanet_server.py"]
    diff --git a/python/examples/network_compute_bridge/fire_extinguisher_server/README.md b/python/examples/network_compute_bridge/fire_extinguisher_server/README.md
    new file mode 100644
    index 000000000..d456d9f01
    --- /dev/null
    +++ b/python/examples/network_compute_bridge/fire_extinguisher_server/README.md
    @@ -0,0 +1,44 @@
    +
    +
    +# Fire Extinguisher Detector Server
    +
    +This is an example of a network compute bridge server. It is similar to tensorflow_server.py, but uses Keras Retinanet instead of TensorFlow. The provided model specifically detects fire extinguishers 
    +
    +
    +## Build and Export
    +This example can be run on a local machine directly, but is easier to use with Docker.
    +
    +The Docker image can be built and exported with the following commands:
    +
    +```
    +# builds the image
    +sudo docker build -t fire_ext_detector .
    +
    +# exports the image, uses pigz
    +sudo docker save fire_ext_detector | pigz > fire_ext_detector.tar.gz
    +```
    +
    +
    +## Execution
    +To run this example on a Spot CORE, run:
    +
    +```
    +./start_server.sh
    +```
    +
    +Otherwise, run:
    +
    +```
    +sudo docker run -d --name retinanet_server --network host --restart unless-stopped fire_ext_detector -d . --username $USERNAME --password $PASSWORD --port $PORT $ROBOT_IP
    +```
    +
    +- `$PORT` is the port to use for the server on the machine the server is running on
    +- `$ROBOT_IP` is the IP address or hostname of your Spot.
    +- `$USERNAME` is the username for the robot
    +- `$PASSWORD` is the password for the robot
    diff --git a/python/examples/network_compute_bridge/fire_extinguisher_server/fire_ext.csv b/python/examples/network_compute_bridge/fire_extinguisher_server/fire_ext.csv
    new file mode 100644
    index 000000000..72335d688
    --- /dev/null
    +++ b/python/examples/network_compute_bridge/fire_extinguisher_server/fire_ext.csv
    @@ -0,0 +1 @@
    +Fire_Extinguisher,7
    \ No newline at end of file
    diff --git a/python/examples/network_compute_bridge/fire_extinguisher_server/fire_ext.h5 b/python/examples/network_compute_bridge/fire_extinguisher_server/fire_ext.h5
    new file mode 100644
    index 000000000..441eb6ed4
    --- /dev/null
    +++ b/python/examples/network_compute_bridge/fire_extinguisher_server/fire_ext.h5
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:3b475935475b2d2a4332c9cf1f4820cd0918d29bcc5ebe68f52ec37c97e4b070
    +size 146687360
    diff --git a/python/examples/network_compute_bridge/fire_extinguisher_server/requirements.txt b/python/examples/network_compute_bridge/fire_extinguisher_server/requirements.txt
    new file mode 100644
    index 000000000..47cbac61c
    --- /dev/null
    +++ b/python/examples/network_compute_bridge/fire_extinguisher_server/requirements.txt
    @@ -0,0 +1,58 @@
    +absl-py==0.11.0
    +astor==0.8.1
    +astunparse==1.6.3
    +bleach==1.5.0
    +bosdyn-api==3.0.0
    +bosdyn-client==3.0.0
    +bosdyn-core==3.0.0
    +bosdyn-mission==3.0.0
    +cached-property==1.5.2
    +cachetools==4.2.1
    +certifi==2020.12.5
    +chardet==4.0.0
    +Cython==0.29.21
    +Deprecated==1.2.11
    +flatbuffers==1.12
    +gast==0.2.2
    +google-auth==1.27.0
    +google-auth-oauthlib==0.4.2
    +google-pasta==0.2.0
    +grpcio==1.32.0
    +h5py==2.10.0
    +html5lib==0.9999999
    +idna==2.10
    +importlib-metadata==3.4.0
    +Keras==2.2.4
    +Keras-Applications==1.0.7
    +Keras-Preprocessing==1.0.9
    +keras-resnet==0.1.0
    +keras-retinanet==0.5.0
    +Markdown==3.3.3
    +mock==4.0.3
    +oauthlib==3.1.0
    +opencv-python==4.0.0.21
    +opt-einsum==3.3.0
    +Pillow==8.1.0
    +progressbar2==3.53.1
    +protobuf==3.14.0
    +pyasn1==0.4.8
    +pyasn1-modules==0.2.8
    +PyJWT==2.0.1
    +python-utils==2.5.6
    +PyYAML==5.4.1
    +requests==2.25.1
    +requests-oauthlib==1.3.0
    +rsa==4.7.1
    +scipy==1.5.4
    +six==1.15.0
    +tensorboard==1.12.2
    +tensorboard-plugin-wit==1.8.0
    +tensorflow==1.12.0
    +tensorflow-estimator==1.13.0
    +tensorflow-tensorboard==1.5.1
    +termcolor==1.1.0
    +typing-extensions==3.7.4.3
    +urllib3==1.26.3
    +Werkzeug==1.0.1
    +wrapt==1.12.1
    +zipp==3.4.0
    diff --git a/python/examples/network_compute_bridge/fire_extinguisher_server/retinanet_server.py b/python/examples/network_compute_bridge/fire_extinguisher_server/retinanet_server.py
    new file mode 100644
    index 000000000..9890e6240
    --- /dev/null
    +++ b/python/examples/network_compute_bridge/fire_extinguisher_server/retinanet_server.py
    @@ -0,0 +1,431 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""Tutorial to show how to use the Boston Dynamics API"""
    +import argparse
    +import io
    +from multiprocessing import Queue
    +import os
    +import sys
    +import time
    +import logging
    +
    +import cv2
    +import numpy as np
    +from PIL import Image
    +from scipy import ndimage
    +
    +from bosdyn.client.directory_registration import (DirectoryRegistrationClient,
    +                                                  DirectoryRegistrationKeepAlive,
    +                                                  ServiceAlreadyExistsError)
    +from bosdyn.client.payload_registration import PayloadRegistrationClient
    +
    +from bosdyn.api import network_compute_bridge_service_pb2_grpc
    +from bosdyn.api import network_compute_bridge_pb2
    +from bosdyn.api import image_pb2
    +import bosdyn.client
    +import bosdyn.client.util
    +import grpc
    +from concurrent import futures
    +
    +# import the necessary packages for ML
    +from keras_retinanet.utils.image import preprocess_image
    +from keras_retinanet.utils.image import resize_image
    +from keras_retinanet import models
    +import argparse
    +from timeit import default_timer as timer
    +
    +import threading
    +from google.protobuf import wrappers_pb2
    +import tensorflow as tf
    +
    +# This is a multiprocessing.Queue for communication between the main process and the
    +# Tensorflow processes.
    +REQUEST_QUEUE = Queue()
    +
    +# This is a multiprocessing.Queue for communication between the Tensorflow processes and
    +# the display process.
    +RESPONSE_QUEUE = Queue()
    +
    +
    +class Resnet50Model:
    +    """ Simple class that allows us to load more than one tensorflow model at once. """
    +
    +    @staticmethod
    +    def loadmodel(path):
    +        return models.load_model(path, backbone_name='resnet50')
    +
    +    def __init__(self, path, labels_path):
    +        self.path = path
    +        self.model = self.loadmodel(path)
    +
    +        # Make sure we tell tensor flow that this is a different model.
    +        self.graph = tf.get_default_graph()
    +
    +        # Load the class label mappings
    +        if labels_path is None:
    +            self.labels = None
    +        else:
    +            # Load the class label mappings
    +            self.labels = open(labels_path).read().strip().split("\n")
    +            self.labels = {int(L.split(",")[1]): L.split(",")[0] for L in self.labels}
    +
    +    def predict(self, X):
    +        """ Predict with this model. """
    +        with self.graph.as_default():
    +            return self.model.predict(X)
    +
    +
    +class KerasExec():
    +    """ Thread that waits for data and runs the Keras model. """
    +
    +    def __init__(self, options, model_extension):
    +        self.in_queue = REQUEST_QUEUE
    +        self.out_queue = RESPONSE_QUEUE
    +        self.options = options
    +        self.debug = not options.no_debug
    +        self.model_extension = model_extension
    +
    +    def run(self):
    +        models = {}
    +        for f in os.listdir(self.options.model_dir):
    +            if f in models:
    +                print('Warning: duplicate model name of "' + f + '", ignoring second model.')
    +                continue
    +
    +            path = os.path.join(self.options.model_dir, f)
    +            if os.path.isfile(path) and path.endswith(self.model_extension):
    +                model_name = ''.join(f.rsplit(self.model_extension, 1)) # remove the extension
    +
    +                # reverse replace the extension
    +                labels_file = '.csv'.join(f.rsplit(self.model_extension, 1))
    +                labels_path = os.path.join(self.options.model_dir, labels_file)
    +
    +                if not os.path.isfile(labels_path):
    +                    labels_path = None
    +                models[model_name] = (Resnet50Model(path, labels_path))
    +
    +        # Tensorflow prints out a bunch of stuff, so print out useful data here after a space.
    +        print('')
    +        print('Running on port: ' + str(self.options.port))
    +        print('Loaded models:')
    +        for model_name in models:
    +            if models[model_name].labels is not None:
    +                labels_str = 'yes'
    +            else:
    +                labels_str = 'no'
    +            print('    ' + model_name + ' (loaded labels: ' + labels_str + ')')
    +
    +        while True:
    +            request = REQUEST_QUEUE.get()
    +
    +            if isinstance(request, network_compute_bridge_pb2.ListAvailableModelsRequest):
    +                out_proto = network_compute_bridge_pb2.ListAvailableModelsResponse()
    +                for model_name in models:
    +                    out_proto.available_models.append(model_name)
    +                    # To show available labels
    +                    #if models[model_name].labels is not None:
    +                    #    labels_msg = out_proto.labels.add()
    +                    #    labels_msg.model_name = model_name
    +                    #    for n in models[model_name].labels:
    +                    #        labels_msg.available_labels.append(models[model_name].labels[n])
    +                RESPONSE_QUEUE.put(out_proto)
    +                continue
    +            else:
    +                out_proto = network_compute_bridge_pb2.NetworkComputeResponse()
    +
    +            # Find the model
    +            if request.input_data.model_name not in models:
    +                print('Cannot find model "' + request.input_data.model_name + '" in loaded models.')
    +                RESPONSE_QUEUE.put(out_proto)
    +                continue
    +
    +            # Got a request, run the model.
    +            self.run_model(request, models[request.input_data.model_name])
    +
    +    def run_model(self, request, this_model):
    +
    +        # Define the out proto
    +        out_proto = network_compute_bridge_pb2.NetworkComputeResponse()
    +
    +        if request.input_data.image.format == image_pb2.Image.FORMAT_RAW:
    +            pil_image = Image.open(io.BytesIO(request.input_data.image.data))
    +            pil_image = ndimage.rotate(pil_image, 0)
    +            if request.input_data.image.pixel_format == image_pb2.Image.PIXEL_FORMAT_GREYSCALE_U8:
    +                image = cv2.cvtColor(pil_image, cv2.COLOR_GRAY2RGB)  # Converted to RGB for Tensorflow
    +            elif request.input_data.image.pixel_format == image_pb2.Image.PIXEL_FORMAT_RGB_U8:
    +                # Already in the correct format
    +                image = pil_image
    +            else:
    +                print('Error: image input in unsupported pixel format: ', request.input_data.image.pixel_format)
    +                RESPONSE_QUEUE.put(out_proto)
    +                return
    +        elif request.input_data.image.format == image_pb2.Image.FORMAT_JPEG:
    +            dtype = np.uint8
    +            jpg = np.frombuffer(request.input_data.image.data, dtype=dtype)
    +            image = cv2.imdecode(jpg, -1)
    +            image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    +
    +            if len(image.shape) < 3:
    +                # Single channel image, convert to RGB.
    +                image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
    +
    +
    +        print('')
    +        print('Starting model eval...')
    +        # Run the model on the image
    +        keras_image = preprocess_image(image)
    +        (keras_image, scale) = resize_image(keras_image)
    +        keras_image = np.expand_dims(keras_image, axis=0)
    +        start = timer()
    +        boxes, scores, classes = this_model.predict(keras_image)
    +        end = timer()
    +        print("Model eval took " + str(end - start) + ' seconds')
    +
    +        boxes /= scale
    +
    +        # Package detections into the output proto
    +        num_objects = 0
    +        boxes = boxes[0]
    +        scores = scores[0]
    +        classes = classes[0]
    +
    +        # Only use the detection w/ the highest confidence for Sensor pointing features
    +        max_conf = max(scores)
    +
    +        print(f"Max conf was {max_conf}")
    +        
    +        for i in range(len(boxes)):
    +            # Skip if not the Fire Extinguisher class
    +            if classes[i] != 7: continue
    +
    +            label = this_model.labels[(classes[i])]
    +            box = boxes[i]
    +            score = scores[i]
    +
    +            if score < max_conf:
    +                # scores are sorted so we can break
    +                continue
    +
    +            num_objects += 1
    +
    +            #draw_box(draw, b, color=color)
    +            print('Found object with label: "' + label + '" and score: ' + str(score))
    +
    +            print(f"Box is {box}")
    +
    +            point1 = np.array([box[0], box[1]])
    +            point2 = np.array([box[0], box[3]])
    +            point3 = np.array([box[2], box[3]])
    +            point4 = np.array([box[2], box[1]])
    +
    +            # Add data to the output proto.
    +            out_obj = out_proto.object_in_image.add()
    +            out_obj.name = label
    +
    +            vertex1 = out_obj.image_properties.coordinates.vertexes.add()
    +            vertex1.x = point1[0]
    +            vertex1.y = point1[1]
    +
    +            vertex2 = out_obj.image_properties.coordinates.vertexes.add()
    +            vertex2.x = point2[0]
    +            vertex2.y = point2[1]
    +
    +            vertex3 = out_obj.image_properties.coordinates.vertexes.add()
    +            vertex3.x = point3[0]
    +            vertex3.y = point3[1]
    +
    +            vertex4 = out_obj.image_properties.coordinates.vertexes.add()
    +            vertex4.x = point4[0]
    +            vertex4.y = point4[1]
    +
    +            # Pack the confidence value.
    +            confidence = wrappers_pb2.FloatValue(value=score)
    +            out_obj.additional_properties.Pack(confidence)
    +
    +            if not self.options.no_debug:
    +
    +                polygon = np.array([point1, point2, point3, point4], np.int32)
    +                polygon = polygon.reshape((-1, 1, 2))
    +                cv2.polylines(image, [polygon], True, (0, 255, 0), 2)
    +
    +                caption = "{}: {:.3f}".format(label, score)
    +                left_x = min(point1[0], min(point2[0], min(point3[0], point4[0])))
    +                top_y = min(point1[1], min(point2[1], min(point3[1], point4[1])))
    +                cv2.putText(image, caption, (int(left_x), int(top_y)), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
    +                            (0, 255, 0), 2)
    +
    +        print('Found ' + str(num_objects) + ' object(s)')
    +
    +        if not self.options.no_debug:
    +            debug_image_filename = 'retinanet_server_output.jpg'
    +            cv2.imwrite(debug_image_filename, cv2.cvtColor(image, cv2.COLOR_RGB2BGR))
    +            print('Wrote debug image output to: "' + debug_image_filename + '"')
    +
    +        # Pack all the outputs up and send them back.
    +        self.out_queue.put(out_proto)
    +
    +
    +class NetworkComputeBridgeWorkerServicer(
    +        network_compute_bridge_service_pb2_grpc.NetworkComputeBridgeWorkerServicer):
    +    """ Class that handles the incoming GRPC messages and dispatches them to a thread that
    +        services them.  Running directly in this callback doesn't work. """
    +
    +    def __init__(self, thread_input_queue, thread_output_queue):
    +        super(NetworkComputeBridgeWorkerServicer, self).__init__()
    +        # Threading input and output queues.
    +        self.thread_input_queue = thread_input_queue
    +        self.thread_output_queue = thread_output_queue
    +
    +    def NetworkCompute(self, request, context):
    +        # Transfer the request to the thread.
    +        self.thread_input_queue.put(request)
    +        # Blocking call, waiting for the thread to respond.
    +        out_proto = self.thread_output_queue.get()
    +        # Returning the proto will send it out over GRPC.
    +        return out_proto
    +
    +    def ListAvailableModels(self, request, context):
    +        self.thread_input_queue.put(request)
    +        out_proto = self.thread_output_queue.get()
    +        return out_proto
    +
    +def get_guid_and_secret():
    +    # Returns the GUID and secret on the Spot CORE
    +    kGuidAndSecretPath = '/opt/payload_credentials/payload_guid_and_secret'
    +    try:
    +        payload_file = open(kGuidAndSecretPath)
    +        guid = payload_file.readline().strip('\n')
    +        secret = payload_file.readline()
    +    except IOError as io_error:
    +        print("Unable to get the GUID/Secret for Spot Core: IOError when reading the file at: "+kGuidAndSecretPath)
    +        raise io_error
    +    return guid, secret
    +
    +def register_with_robot(options):
    +    """ Registers this worker with the robot's Directory."""
    +    ip = bosdyn.client.common.get_self_ip(options.hostname)
    +    print('Detected IP address as: ' + ip)
    +    kServiceName = "fire-extinguisher-server"
    +    kServiceTypeName = "bosdyn.api.NetworkComputeBridgeWorker"
    +    kServiceAuthority = "fire-extinguisher-worker.spot.robot"
    +
    +    sdk = bosdyn.client.create_standard_sdk("retinanet-server")
    +
    +    robot = sdk.create_robot(options.hostname)
    +
    +    # Authenticate robot before being able to use it
    +    if options.on_spot_core:
    +        guid, secret = get_guid_and_secret()
    +        robot.authenticate_from_payload_credentials(guid, secret)
    +    else:
    +        robot.authenticate(options.username, options.password)
    +
    +    directory_client = robot.ensure_client(
    +        bosdyn.client.directory.DirectoryClient.default_service_name)
    +    directory_registration_client = robot.ensure_client(
    +        bosdyn.client.directory_registration.DirectoryRegistrationClient.default_service_name)
    +
    +    # Create a keep_alive to reset and maintain registration of service.
    +    keep_alive = DirectoryRegistrationKeepAlive(directory_registration_client)
    +    keep_alive.start(kServiceName, kServiceTypeName, kServiceAuthority, ip, int(options.port))
    +
    +    # List all services. Success if above test service is shown.
    +    try:
    +        registered_service = directory_client.get_entry(kServiceName)
    +    except NonexistentServiceError:
    +        print('\nSelf-registered service not found. Failure.')
    +        return False
    +
    +    print('\nService registration confirmed. Self-registration was a success.')
    +    return True
    +
    +
    +def main(argv):
    +    """Command line interface.
    +
    +    Args:
    +        argv: List of command-line arguments passed to the program.
    +    """
    +
    +    # construct the argument parse and parse the arguments
    +    ap = argparse.ArgumentParser()
    +
    +    default_port = '50051'
    +    model_extension = '.h5'
    +
    +    parser = argparse.ArgumentParser()
    +    parser.add_argument(
    +        '-d', '--model-dir', help=
    +        'Directory of pre-trained models and (optionally) associated label files.\nExample directory contents: my_model.pb, my_classes.csv, my_model2.pb, my_classes2.csv.  CSV label format is: object,1thing,2', required=True
    +    )
    +    parser.add_argument('-p', '--port', help='Server\'s port number, default: ' + default_port,
    +                        default=default_port)
    +    parser.add_argument('-n', '--no-debug', help='Disable writing debug images.', action='store_true')
    +    parser.add_argument('-r', '--no-registration', help='Don\'t register with the robot\'s directory. This is useful for cloud applications where we can\'t reach into every robot directly. Instead use another program to register this server.', action='store_true')
    +    parser.add_argument('--username', help='User name of account to get credentials for.')
    +    parser.add_argument('--password', help='Password to get credentials for.')
    +    parser.add_argument('--on-spot-core', help='Use SpotCore GUID to register the server with Spot', action='store_true')
    +    parser.add_argument('hostname', nargs='?', help='Hostname or address of robot,'
    +                        ' e.g. "beta25-p" or "192.168.80.3"')
    +    options = parser.parse_args(argv)
    +
    +    # Either we need a hostname to talk to the robot or the --no-registration argument.
    +    if not options.no_registration and (options.hostname is None or len(options.hostname) < 1):
    +        print('Error: must either provide a robot hostname or the --no-registration argument.')
    +        sys.exit(1)
    +
    +    if options.no_registration and (options.hostname is not None and len(options.hostname) > 0):
    +        print('Error: cannot provide both a robot hostname and the --no-registration argument.')
    +        sys.exit(1)
    +
    +    if not os.path.isdir(options.model_dir):
    +        print('Error: model directory (' + options.model_dir + ') not found or is not a directory.')
    +        sys.exit(1)
    +
    +    # Make sure there is at least one file ending in .pb in the directory.
    +    found_model = False
    +    for f in os.listdir(options.model_dir):
    +        path = os.path.join(options.model_dir, f)
    +        if os.path.isfile(path) and path.endswith(model_extension):
    +            found_model = True
    +            break
    +
    +    if not found_model:
    +        print('Error: model directory must contain at least one model file with extension ' + model_extension + '.  Found:')
    +        for f in os.listdir(options.model_dir):
    +            print('    ' + f)
    +        sys.exit(1)
    +
    +    if not options.no_registration:
    +        register_with_robot(options)
    +
    +    # Start the model eval thread.
    +    keras_exec = KerasExec(options, model_extension)
    +    model_thread = threading.Thread(target=keras_exec.run)
    +    print("Starting Model Thread")
    +    model_thread.start()
    +
    +    # Start the GRPC server
    +    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    +    network_compute_bridge_service_pb2_grpc.add_NetworkComputeBridgeWorkerServicer_to_server(
    +        NetworkComputeBridgeWorkerServicer(keras_exec.in_queue, keras_exec.out_queue), server)
    +    server.add_insecure_port('[::]:' + options.port)
    +    server.start()
    +
    +    print('Running...')
    +    while True:
    +        print('.', end="")
    +        sys.stdout.flush()
    +        time.sleep(2)
    +
    +    return True
    +
    +
    +if __name__ == '__main__':
    +    logging.basicConfig()
    +    if not main(sys.argv[1:]):
    +        sys.exit(1)
    diff --git a/python/examples/network_compute_bridge/fire_extinguisher_server/start_server.sh b/python/examples/network_compute_bridge/fire_extinguisher_server/start_server.sh
    new file mode 100644
    index 000000000..6f3cdbaeb
    --- /dev/null
    +++ b/python/examples/network_compute_bridge/fire_extinguisher_server/start_server.sh
    @@ -0,0 +1,13 @@
    +#!/bin/bash
    +ROBOT_IP=192.168.50.3
    +USERNAME=
    +PASSWORD=
    +PAYLOAD_CRED_PATH=/opt/payload_credentials/payload_guid_and_secret
    +PORT=50055
    +
    +# Load the docker image
    +sudo docker load -i fireextinguisherserver.tar
    +# Start the server with robot username and password
    +# sudo docker run -d --name retinanet_server --network host --restart unless-stopped -v $PAYLOAD_CRED_PATH:$PAYLOAD_CRED_PATH fireextinguisherserver -d . --username $USERNAME --password $PASSWORD $ROBOT_IP
    +# Start the server with spotcore credentials
    +sudo docker run -d --name retinanet_server --network host --restart unless-stopped -v $PAYLOAD_CRED_PATH:$PAYLOAD_CRED_PATH fireextinguisherserver -d . --on-spot-core --port $PORT $ROBOT_IP
    \ No newline at end of file
    diff --git a/python/examples/network_compute_bridge/identify_object.py b/python/examples/network_compute_bridge/identify_object.py
    index f489bf588..3897744ff 100644
    --- a/python/examples/network_compute_bridge/identify_object.py
    +++ b/python/examples/network_compute_bridge/identify_object.py
    @@ -39,6 +39,7 @@
     import cv2
     import numpy as np
     
    +
     def get_all_network_compute_services(directory_client):
         dir_list = directory_client.list()
         out = []
    @@ -49,20 +50,31 @@ def get_all_network_compute_services(directory_client):
     
         return out
     
    +
     def main(argv):
         """An example using the API to list and get specific objects."""
         parser = argparse.ArgumentParser()
         bosdyn.client.util.add_common_arguments(parser)
         parser.add_argument('-i', '--image-source', help='Image source on the robot to use.')
    -    parser.add_argument('-q', '--image-source-service', help='Image *service* for the image source to use.  Defaults to the main image service if not provided.', default='')
    -    parser.add_argument('-s', '--service', help='Service name of external machine learning server in the directory.',
    +    parser.add_argument(
    +        '-q', '--image-source-service', help=
    +        'Image *service* for the image source to use.  Defaults to the main image service if not provided.',
    +        default='')
    +    parser.add_argument('-s', '--service',
    +                        help='Service name of external machine learning server in the directory.',
                             required=False)
         parser.add_argument('-m', '--model', help='Model file on the server')
         parser.add_argument('-c', '--confidence', help='Minimum confidence to return an object.',
                             default=0.5, type=float)
    -    parser.add_argument('-j', '--input-image', help='Path to an image to use instead of an image source.')
    -    parser.add_argument('-l', '--model-list', help='List all available network compute servers and their provided models.', action='store_true')
    -    parser.add_argument('-r', '--disable-rotation', help='Disable rotation of images (to align with horizontal)', action='store_true')
    +    parser.add_argument('-j', '--input-image',
    +                        help='Path to an image to use instead of an image source.')
    +    parser.add_argument(
    +        '-l', '--model-list',
    +        help='List all available network compute servers and their provided models.',
    +        action='store_true')
    +    parser.add_argument('-r', '--disable-rotation',
    +                        help='Disable rotation of images (to align with horizontal)',
    +                        action='store_true')
         options = parser.parse_args(argv)
     
         if options.image_source is not None and options.input_image is not None:
    @@ -96,12 +108,11 @@ def main(argv):
         robot_command_client = robot.ensure_client(RobotCommandClient.default_service_name)
         robot.time_sync.wait_for_sync()
     
    -
    -
         if options.model_list:
             server_service_names = get_all_network_compute_services(directory_client)
     
    -        print('Found ' + str(len(server_service_names)) + ' available service(s).  Listing their models:')
    +        print('Found ' + str(len(server_service_names)) +
    +              ' available service(s).  Listing their models:')
             print('------------------------------------')
     
             for service in server_service_names:
    @@ -133,8 +144,7 @@ def main(argv):
                 sys.exit(1)
     
             img_source_and_service = network_compute_bridge_pb2.ImageSourceAndService(
    -            image_source = options.image_source,
    -            image_service = options.image_source_service)
    +            image_source=options.image_source, image_service=options.image_source_service)
     
             input_data = network_compute_bridge_pb2.NetworkComputeInputData(
                 image_source_and_service=img_source_and_service, model_name=options.model,
    @@ -157,11 +167,12 @@ def main(argv):
             height = image_in.shape[0]
             width = image_in.shape[1]
     
    -        image_proto = image_pb2.Image(format=image_pb2.Image.FORMAT_JPEG, cols=width, rows=height, data=im_buffer.tobytes(), pixel_format=image_pb2.Image.PIXEL_FORMAT_RGB_U8)
    +        image_proto = image_pb2.Image(format=image_pb2.Image.FORMAT_JPEG, cols=width, rows=height,
    +                                      data=im_buffer.tobytes(),
    +                                      pixel_format=image_pb2.Image.PIXEL_FORMAT_RGB_U8)
     
             input_data = network_compute_bridge_pb2.NetworkComputeInputData(
    -            image=image_proto, model_name=options.model,
    -            min_confidence=options.confidence)
    +            image=image_proto, model_name=options.model, min_confidence=options.confidence)
     
         if options.disable_rotation:
             input_data.rotate_image = network_compute_bridge_pb2.NetworkComputeInputData.ROTATE_IMAGE_NO_ROTATION
    @@ -192,6 +203,10 @@ def main(argv):
             # image.
             img = image_in
     
    +    # The image always comes back in the raw orientation.  Rotate it to horizontal so that we can
    +    # visualize it the same as it was processed.
    +    img, rotmat = rotate_image_nocrop(img, response.image_rotation_angle)
    +
         # Convert to color for nicer drawing
         if len(img.shape) < 3:
             img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
    @@ -207,9 +222,12 @@ def main(argv):
             min_x = float('inf')
             min_y = float('inf')
             for v in obj.image_properties.coordinates.vertexes:
    -            polygon.append([v.x, v.y])
    -            min_x = min(min_x, v.x)
    -            min_y = min(min_y, v.y)
    +            # If we are rotating the output image, make sure to rotate the bounding box points
    +            # as well
    +            x, y = rotate_point(v.x, v.y, rotmat)
    +            polygon.append([x, y])
    +            min_x = min(min_x, x)
    +            min_y = min(min_y, y)
     
             polygon = np.array(polygon, np.int32)
             polygon = polygon.reshape((-1, 1, 2))
    @@ -221,6 +239,35 @@ def main(argv):
     
         cv2.imwrite('identify_object_output.jpg', img)
     
    +def rotate_image_nocrop(img, rotation_rad):
    +    """ Rotate an image without cropping (instead adding black space) """
    +    h = img.shape[0]
    +    w = img.shape[1]
    +    cX = w / 2
    +    cY = h / 2
    +
    +    rotmat = cv2.getRotationMatrix2D((cX, cY), math.degrees(rotation_rad), 1.0)
    +
    +    cos_d = abs(rotmat[0][0])
    +    sin_d = abs(rotmat[0][1])
    +
    +    # Compute the new bounding dimensions
    +    nW = int( (h*sin_d) + (w*cos_d) )
    +    nH = int( (h*cos_d) + (w*sin_d) )
    +
    +    # Adjust the rotation matrix to account for translation
    +    rotmat[0][2] += (nW / 2.0) - cX
    +    rotmat[1][2] += (nH / 2.0) - cY
    +
    +    img = cv2.warpAffine(img, rotmat, (nW, nH))
    +
    +    return img, rotmat
    +
    +def rotate_point(x, y, rotmat):
    +    """ Apply a rotation matrix to a point """
    +    src = np.array([[[x, y]]])
    +    out = cv2.transform(src, rotmat)
    +    return out[0][0]
     
     if __name__ == '__main__':
         if not main(sys.argv[1:]):
    diff --git a/python/examples/network_compute_bridge/identify_object_without_robot.py b/python/examples/network_compute_bridge/identify_object_without_robot.py
    new file mode 100644
    index 000000000..fc94b8f1d
    --- /dev/null
    +++ b/python/examples/network_compute_bridge/identify_object_without_robot.py
    @@ -0,0 +1,163 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +import argparse
    +import sys
    +import numpy as np
    +import grpc
    +import multiprocessing as mp
    +import os
    +import time
    +
    +from bosdyn.api import image_pb2
    +from bosdyn.api import network_compute_bridge_pb2
    +from bosdyn.api import network_compute_bridge_service_pb2
    +from bosdyn.api import network_compute_bridge_service_pb2_grpc
    +from google.protobuf import wrappers_pb2
    +
    +import cv2
    +import numpy as np
    +
    +
    +def append_str_to_filename(filename, string):
    +    return "{0}_{2}{1}".format(*os.path.splitext(filename) + (string,))
    +
    +
    +def _send_request(server, image_path, model, confidence, verbose=False):
    +    start = time.time()
    +
    +    channel = grpc.insecure_channel(server)
    +    stub = network_compute_bridge_service_pb2_grpc.NetworkComputeBridgeWorkerStub(channel)
    +    server_data = network_compute_bridge_pb2.NetworkComputeServerConfiguration(service_name='test')
    +    # Read the input image.
    +    image_in = cv2.imread(image_path)
    +    if image_in is None:
    +        print('Error: failed to read "' + image_path + '".  Does the file exist?')
    +        sys.exit(1)
    +
    +    rgb = cv2.cvtColor(image_in, cv2.COLOR_BGR2RGB)
    +
    +    success, im_buffer = cv2.imencode(".jpg", rgb)
    +
    +    if not success:
    +        print('Error: failed to encode input image as a jpg.  Abort.')
    +        sys.exit(1)
    +
    +    height = image_in.shape[0]
    +    width = image_in.shape[1]
    +
    +    image_proto = image_pb2.Image(format=image_pb2.Image.FORMAT_JPEG, cols=width, rows=height,
    +                                  data=im_buffer.tobytes(),
    +                                  pixel_format=image_pb2.Image.PIXEL_FORMAT_RGB_U8)
    +
    +    input_data = network_compute_bridge_pb2.NetworkComputeInputData(image=image_proto,
    +                                                                    model_name=model,
    +                                                                    min_confidence=confidence)
    +
    +    process_img_req = network_compute_bridge_pb2.NetworkComputeRequest(
    +        input_data=input_data, server_config=server_data)
    +
    +    response = stub.NetworkCompute(process_img_req)
    +    end = time.time()
    +    latency = end - start
    +    print(f'latency: {latency * 1000} ms')
    +
    +    if verbose:
    +        if len(response.object_in_image) <= 0:
    +            print('No objects found')
    +        else:
    +            print('Got ' + str(len(response.object_in_image)) + ' objects.')
    +
    +        # Draw bounding boxes in the image for all the detections.
    +        for obj in response.object_in_image:
    +            print(obj)
    +            conf_msg = wrappers_pb2.FloatValue()
    +            obj.additional_properties.Unpack(conf_msg)
    +            confidence = conf_msg.value
    +
    +            polygon = []
    +            min_x = float('inf')
    +            min_y = float('inf')
    +            for v in obj.image_properties.coordinates.vertexes:
    +                polygon.append([v.x, v.y])
    +                min_x = min(min_x, v.x)
    +                min_y = min(min_y, v.y)
    +
    +            polygon = np.array(polygon, np.int32)
    +            polygon = polygon.reshape((-1, 1, 2))
    +            cv2.polylines(image_in, [polygon], True, (0, 255, 0), 2)
    +
    +            caption = "{} {:.3f}".format(obj.name, confidence)
    +            cv2.putText(image_in, caption, (int(min_x), int(min_y)), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
    +                        (0, 255, 0), 2)
    +
    +        cv2.imwrite(append_str_to_filename(image_path, 'detections'), image_in)
    +    return latency
    +
    +
    +def main(argv):
    +    """An example using the API to list and get specific objects."""
    +    parser = argparse.ArgumentParser()
    +    parser.add_argument('-s', '--server', help='IP address of external machine learning server.',
    +                        required=True)
    +    parser.add_argument('-m', '--model', help='Model file on the server', default='coco_inference')
    +    parser.add_argument('-c', '--confidence', help='Minimum confidence to return an object.',
    +                        default=0.7, type=float)
    +    parser.add_argument('-j', '--input-image-dir', help='Path to a directory of images.')
    +    parser.add_argument('-n', '--num-runs', help='Number of runs of the image directory to perform',
    +                        default=1, type=int)
    +    parser.add_argument('-l', '--model-list', help='List of models to be used.',
    +                        action='store_true')
    +    parser.add_argument('-v', '--verbose', help='Print verbose output', action='store_true')
    +    options = parser.parse_args(argv)
    +
    +    if options.input_image_dir is None and not options.model_list:
    +        print('Error: must provide an input image.')
    +        sys.exit(1)
    +
    +    if options.model_list and options.input_image_dir is not None:
    +        print('Error: cannot list models with input image.')
    +        sys.exit(1)
    +
    +    if options.model_list:
    +        channel = grpc.insecure_channel(options.server)
    +        stub = network_compute_bridge_service_pb2_grpc.NetworkComputeBridgeWorkerStub(channel)
    +        server_data = network_compute_bridge_pb2.NetworkComputeServerConfiguration(
    +            service_name='test')
    +        list_req = network_compute_bridge_pb2.ListAvailableModelsRequest(server_config=server_data)
    +        response = stub.ListAvailableModels(list_req)
    +
    +        print('Available models on server at ' + options.server + ' are:')
    +        for model in response.available_models:
    +            print('    ' + model)
    +        sys.exit(0)
    +
    +    image_paths = []
    +    for entry in os.scandir(options.input_image_dir):
    +        if (entry.path.endswith(".jpg") or entry.path.endswith(".png")) and entry.is_file():
    +            image_paths.append(entry.path)
    +
    +    image_paths = image_paths * options.num_runs
    +
    +    start_time = time.perf_counter()
    +    with mp.Pool() as p:
    +        latencies = p.starmap(
    +            _send_request,
    +            [(options.server, path, options.model, options.confidence, options.verbose)
    +             for path in image_paths])
    +        latency_sum = sum(latencies)
    +        count = len(latencies)
    +
    +    end_time = time.perf_counter()
    +    total_time = end_time - start_time
    +    print(f'Total time: {total_time} seconds.')
    +    avg_latency = latency_sum / count
    +    print(f'avg latency: {avg_latency * 1000} ms, fps: {count / total_time} fps')
    +
    +
    +if __name__ == '__main__':
    +    if not main(sys.argv[1:]):
    +        sys.exit(1)
    diff --git a/python/examples/network_compute_bridge/register_remote_server.py b/python/examples/network_compute_bridge/register_remote_server.py
    index ea0058b37..f1b513bc8 100644
    --- a/python/examples/network_compute_bridge/register_remote_server.py
    +++ b/python/examples/network_compute_bridge/register_remote_server.py
    @@ -19,7 +19,6 @@
     import bosdyn.api.directory_registration_pb2 as directory_registration_protos
     
     
    -
     def main(argv):
         """Command line interface.
     
    @@ -28,28 +27,21 @@ def main(argv):
         """
     
         parser = argparse.ArgumentParser()
    -    parser.add_argument(
    -        '-i', '--server-ip', help=
    -        'IP address of the server to register', required=True
    -    )
    -    parser.add_argument(
    -        '-p', '--server-port', help=
    -        'Port of the server to register', required=True
    -    )
    -    parser.add_argument(
    -        '-n', '--service-name', help=
    -        'Name for the service you are registering', required=True
    -    )
    -    parser.add_argument(
    -        '-f', '--force', help=
    -        'Overwrite existing clients using this name.', action='store_true'
    -    )
    +    parser.add_argument('-i', '--server-ip', help='IP address of the server to register',
    +                        required=True)
    +    parser.add_argument('-p', '--server-port', help='Port of the server to register', required=True)
    +    parser.add_argument('-n', '--service-name', help='Name for the service you are registering',
    +                        required=True)
    +    parser.add_argument('-a', '--authority', help='Authority for the service you are registering',
    +                        required=False)
    +    parser.add_argument('-f', '--force', help='Overwrite existing clients using this name.',
    +                        action='store_true')
     
         bosdyn.client.util.add_common_arguments(parser)
     
         options = parser.parse_args(argv)
     
    -    kServiceAuthority = "auth.spot.robot"
    +    kServiceAuthority = options.authority or "remote-server-worker.spot.robot"
     
         sdk = bosdyn.client.create_standard_sdk("register_remote_server")
     
    @@ -79,8 +71,12 @@ def main(argv):
                 break
     
         # Register service
    -    print('Attempting to register ' + options.server_ip + ':' + options.server_port + ' onto ' + options.hostname + ' directory...')
    -    directory_registration_client.register(options.service_name, "bosdyn.api.NetworkComputeBridgeWorker", kServiceAuthority, options.server_ip, int(options.server_port))
    +    print('Attempting to register ' + options.server_ip + ':' + options.server_port + ' onto ' +
    +          options.hostname + ' directory...')
    +    directory_registration_client.register(options.service_name,
    +                                           "bosdyn.api.NetworkComputeBridgeWorker",
    +                                           kServiceAuthority, options.server_ip,
    +                                           int(options.server_port))
         print('Done.')
     
         return True
    diff --git a/python/examples/network_compute_bridge/requirements_client.txt b/python/examples/network_compute_bridge/requirements_client.txt
    new file mode 100644
    index 000000000..2752c5efc
    --- /dev/null
    +++ b/python/examples/network_compute_bridge/requirements_client.txt
    @@ -0,0 +1,5 @@
    +-f ../../../prebuilt
    +bosdyn-client>=2.3
    +
    +opencv-python>=3.4.2.17
    +numpy>=1.16.4
    diff --git a/python/examples/network_compute_bridge/requirements_server_cpu.txt b/python/examples/network_compute_bridge/requirements_server_cpu.txt
    new file mode 100644
    index 000000000..00bdf44b8
    --- /dev/null
    +++ b/python/examples/network_compute_bridge/requirements_server_cpu.txt
    @@ -0,0 +1,8 @@
    +-f ../../../prebuilt
    +bosdyn-client>=2.3
    +
    +opencv-python>=3.4.2.17
    +numpy>=1.16.4
    +Pillow==6.1.0
    +scipy>=1.5.2
    +tensorflow==2.0.4
    diff --git a/python/examples/network_compute_bridge/requirements_server_gpu.txt b/python/examples/network_compute_bridge/requirements_server_gpu.txt
    new file mode 100644
    index 000000000..feb8bf394
    --- /dev/null
    +++ b/python/examples/network_compute_bridge/requirements_server_gpu.txt
    @@ -0,0 +1,8 @@
    +-f ../../../prebuilt
    +bosdyn-client>=2.3
    +
    +opencv-python>=3.4.2.17
    +numpy>=1.16.4
    +Pillow==6.1.0
    +scipy>=1.5.2
    +tensorflow-gpu==2.0.4
    diff --git a/python/examples/network_compute_bridge/requirements_tensorflow_server_cpu.txt b/python/examples/network_compute_bridge/requirements_tensorflow_server_cpu.txt
    deleted file mode 100644
    index b4ee12625..000000000
    --- a/python/examples/network_compute_bridge/requirements_tensorflow_server_cpu.txt
    +++ /dev/null
    @@ -1,8 +0,0 @@
    --f ../../../prebuilt
    -bosdyn-client ~= 2.3
    -
    -opencv-python >= 3.4.2.17
    -numpy >= 1.16.4
    -Pillow==6.1.0
    -scipy >= 1.5.2
    -tensorflow ~= 2.0.4
    diff --git a/python/examples/network_compute_bridge/requirements_tensorflow_server_gpu.txt b/python/examples/network_compute_bridge/requirements_tensorflow_server_gpu.txt
    deleted file mode 100644
    index ec316fd67..000000000
    --- a/python/examples/network_compute_bridge/requirements_tensorflow_server_gpu.txt
    +++ /dev/null
    @@ -1,8 +0,0 @@
    --f ../../../prebuilt
    -bosdyn-client ~= 2.3
    -
    -opencv-python >= 3.4.2.17
    -numpy >= 1.16.4
    -Pillow==6.1.0
    -scipy >= 1.5.2
    -tensorflow-gpu ~= 2.0.4
    diff --git a/python/examples/network_compute_bridge/tensorflow_server.py b/python/examples/network_compute_bridge/tensorflow_server.py
    index 4d68db348..a7c0b4bf5 100644
    --- a/python/examples/network_compute_bridge/tensorflow_server.py
    +++ b/python/examples/network_compute_bridge/tensorflow_server.py
    @@ -66,6 +66,7 @@
     # the display process.
     RESPONSE_QUEUE = Queue()
     
    +
     class TensorflowModel:
         """ Wraps a tensorflow model in a way that allows online switching between models."""
     
    @@ -108,10 +109,9 @@ def predict(self, image):
                 image_np_expanded = np.expand_dims(image, axis=0)
                 # Actual detection.
                 (boxes, scores, classes, num) = self.sess.run([
    -                self.detection_boxes, self.detection_scores, self.detection_classes, self.num_detections
    -            ], feed_dict={
    -                self.image_tensor: image_np_expanded
    -            })
    +                self.detection_boxes, self.detection_scores, self.detection_classes,
    +                self.num_detections
    +            ], feed_dict={self.image_tensor: image_np_expanded})
     
                 im_height, im_width, _ = image.shape
                 boxes_list = [None for i in range(boxes.shape[1])]
    @@ -127,8 +127,6 @@ def predict(self, image):
                 return boxes_list, scores[0].tolist(), labels_out, int(num[0])
     
     
    -
    -
     def start_tensorflow_processes(options, model_extension):
         """Starts Tensorflow processes in parallel.
     
    @@ -151,7 +149,7 @@ def process_images(options, model_extension):
     
             path = os.path.join(options.model_dir, f)
             if os.path.isfile(path) and path.endswith(model_extension):
    -            model_name = ''.join(f.rsplit(model_extension, 1)) # remove the extension
    +            model_name = ''.join(f.rsplit(model_extension, 1))  # remove the extension
     
                 # reverse replace the extension
                 labels_file = '.csv'.join(f.rsplit(model_extension, 1))
    @@ -201,7 +199,8 @@ def process_images(options, model_extension):
                 pil_image = Image.open(io.BytesIO(request.input_data.image.data))
                 pil_image = ndimage.rotate(pil_image, 0)
                 if request.input_data.image.pixel_format == image_pb2.Image.PIXEL_FORMAT_GREYSCALE_U8:
    -                image = cv2.cvtColor(pil_image, cv2.COLOR_GRAY2RGB)  # Converted to RGB for Tensorflow
    +                image = cv2.cvtColor(pil_image,
    +                                     cv2.COLOR_GRAY2RGB)  # Converted to RGB for Tensorflow
                 elif request.input_data.image.pixel_format == image_pb2.Image.PIXEL_FORMAT_RGB_U8:
                     # Already in the correct format
                     image = pil_image
    @@ -280,8 +279,8 @@ def process_images(options, model_extension):
                     caption = "{}: {:.3f}".format(label, score)
                     left_x = min(point1[0], min(point2[0], min(point3[0], point4[0])))
                     top_y = min(point1[1], min(point2[1], min(point3[1], point4[1])))
    -                cv2.putText(image, caption, (int(left_x), int(top_y)), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
    -                            (0, 255, 0), 2)
    +                cv2.putText(image, caption, (int(left_x), int(top_y)), cv2.FONT_HERSHEY_SIMPLEX,
    +                            0.5, (0, 255, 0), 2)
     
             print('Found ' + str(num_objects) + ' object(s)')
     
    @@ -312,12 +311,13 @@ def ListAvailableModels(self, request, context):
             out_proto = self.thread_output_queue.get()
             return out_proto
     
    +
     def register_with_robot(options):
         """ Registers this worker with the robot's Directory."""
         ip = bosdyn.client.common.get_self_ip(options.hostname)
         print('Detected IP address as: ' + ip)
         kServiceName = "tensorflow-server"
    -    kServiceAuthority = "auth.spot.robot"
    +    kServiceAuthority = "tensorflow-worker.spot.robot"
     
         sdk = bosdyn.client.create_standard_sdk("tensorflow_server")
     
    @@ -340,9 +340,10 @@ def register_with_robot(options):
                 break
     
         # Register service
    -    print('Attempting to register ' + ip + ':' + options.port + ' onto ' + options.hostname + ' directory...')
    -    directory_registration_client.register(kServiceName, "bosdyn.api.NetworkComputeBridgeWorker", kServiceAuthority, ip, int(options.port))
    -
    +    print('Attempting to register ' + ip + ':' + options.port + ' onto ' + options.hostname +
    +          ' directory...')
    +    directory_registration_client.register(kServiceName, "bosdyn.api.NetworkComputeBridgeWorker",
    +                                           kServiceAuthority, ip, int(options.port))
     
     
     def main(argv):
    @@ -358,12 +359,16 @@ def main(argv):
         parser = argparse.ArgumentParser()
         parser.add_argument(
             '-d', '--model-dir', help=
    -        'Directory of pre-trained models and (optionally) associated label files.\nExample directory contents: my_model.pb, my_classes.csv, my_model2.pb, my_classes2.csv.  CSV label format is: object,1thing,2', required=True
    -    )
    +        'Directory of pre-trained models and (optionally) associated label files.\nExample directory contents: my_model.pb, my_classes.csv, my_model2.pb, my_classes2.csv.  CSV label format is: object,1thing,2',
    +        required=True)
         parser.add_argument('-p', '--port', help='Server\'s port number, default: ' + default_port,
                             default=default_port)
    -    parser.add_argument('-n', '--no-debug', help='Disable writing debug images.', action='store_true')
    -    parser.add_argument('-r', '--no-registration', help='Don\'t register with the robot\'s directory. This is useful for cloud applications where we can\'t reach into every robot directly. Instead use another program to register this server.', action='store_true')
    +    parser.add_argument('-n', '--no-debug', help='Disable writing debug images.',
    +                        action='store_true')
    +    parser.add_argument(
    +        '-r', '--no-registration', help=
    +        'Don\'t register with the robot\'s directory. This is useful for cloud applications where we can\'t reach into every robot directly. Instead use another program to register this server.',
    +        action='store_true')
     
         parser.add_argument('--username', help='User name of account to get credentials for.')
         parser.add_argument('--password', help='Password to get credentials for.')
    @@ -394,7 +399,8 @@ def main(argv):
                 break
     
         if not found_model:
    -        print('Error: model directory must contain at least one model file with extension ' + model_extension + '.  Found:')
    +        print('Error: model directory must contain at least one model file with extension ' +
    +              model_extension + '.  Found:')
             for f in os.listdir(options.model_dir):
                 print('    ' + f)
             sys.exit(1)
    diff --git a/python/examples/payloads/README.md b/python/examples/payloads/README.md
    index 15db93c2c..c3e62ff9d 100644
    --- a/python/examples/payloads/README.md
    +++ b/python/examples/payloads/README.md
    @@ -8,17 +8,38 @@ Development Kit License (20191101-BDSDK-SL).
     
     # Using the Payload Service
     
    -This example program demonstrates how to create a payload and register this new payload with the payload service. As well, the example program shows how to list all payloads registered with Spot's payload service, which should include the newly registered payload created in the example.
    +These example programs demonstrate how to use the payload service. 
    +
    +The payload.py example demonstrates how to create a payload and register this new payload with the payload service, and also how to list all payloads registered with Spot's payload service. The list should include the newly registered payload created in the example.
    +
    +The attach_detach_payload.py example demonstrates how to tell the robot that a payload is attached or detached through the service. This can also be done via the webserver. This is particularly useful if you are attaching and removing payloads from the robot while the robot is working (like picking up a sensor in the gripper), and you'd like to inform the robot of the updates programmatically.
     
     ## Setup Dependencies
    -This example requires the bosdyn API and client to be installed, and must be run using python3. Using pip, these dependencies can be installed using:
    +These examples require the bosdyn API and client to be installed, and must be run using python3. Using pip, these dependencies can be installed using:
     
     ```
     python3 -m pip install -r requirements.txt
     ```
     
    -## Running the Example
    -To run the example:
    +## Running the Payload Registration Example
    +To run the payload registration example:
     ```
     python3 payloads.py --username USER --password PASSWORD ROBOT_IP
     ```
    +
    +## Running the Attach or Detach Payload Example
    +A payload must be registered and authorized before it can be attached and detached over the API. 
    +
    +1. Register a payload, e.g. using the above example. When you register the payload, save the GUID and secret that you choose for the payload. In the above example, these are set to variable names `payload.GUID` and `payload_secret`.
    +2. Use the Spot webserver to "authorize" the payload. Open up the webserver, select the 'Payloads' tab, then click 'authorize' for the payload you wish to authorize. If a payload has multiple presets associated with it, you will be prompted to select a preset before authorizing. 
    +3. Run the example with one of the following calls, passing the guid and secret as arguments to the script: 
    +
    +To attach the payload, run: 
    +```
    +python3 attach_detach_payload.py ROBOT_IP --guid GUID --secret SECRET --attach 
    +```
    +
    +To detach the payload, run: 
    +```
    +python3 attach_detach_payload.py ROBOT_IP --guid GUID --secret SECRET --detach 
    +```
    \ No newline at end of file
    diff --git a/python/examples/payloads/attach_detach_payload.py b/python/examples/payloads/attach_detach_payload.py
    new file mode 100644
    index 000000000..cd355b97b
    --- /dev/null
    +++ b/python/examples/payloads/attach_detach_payload.py
    @@ -0,0 +1,78 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""
    +Code for attaching and detaching a payload via the payload service API
    +"""
    +from __future__ import print_function
    +import argparse
    +import sys
    +import logging
    +
    +import bosdyn.client
    +from bosdyn.client.payload import PayloadClient
    +from bosdyn.client.payload_registration import PayloadRegistrationClient
    +import bosdyn.client.util
    +
    +LOGGER = logging.getLogger()
    +
    +
    +def payload_attach_detach(config):
    +    """A simple example of using the Boston Dynamics API to attach and detach payloads on Spot.
    +
    +    A payload may only be modified in this way if it is authorized. See the readme for more information. 
    +
    +    First, this example uses a payload registration client to attach or detach the payload. Then, 
    +    this example uses a payload client to list all of the payloads currently on the robot. 
    +    """
    +    sdk = bosdyn.client.create_standard_sdk('SpotPayloadAttachDetachClient')
    +
    +    robot = sdk.create_robot(config.hostname)
    +
    +    # Get a payload registration client
    +    payload_registration_client = robot.ensure_client(
    +        PayloadRegistrationClient.default_service_name)
    +
    +    if (config.detach):
    +        # Call the helper function to detach a payload from the robot
    +        payload_registration_client.detach_payload(config.guid, config.secret)
    +    elif (config.attach):
    +        # Call the helper function to attach a payload to the robot
    +        payload_registration_client.attach_payload(config.guid, config.secret)
    +
    +    # Get a token using the guid & secret of the payload
    +    token = payload_registration_client.get_payload_auth_token(config.guid, config.secret)
    +
    +    # Authenticate the robot before being able to get a payload client
    +    robot.authenticate_with_token(token)
    +
    +    # Get a payload client
    +    payload_client = robot.ensure_client(PayloadClient.default_service_name)
    +
    +    # List all payloads using the payload client
    +    payloads = payload_client.list_payloads()
    +    print('\n\n Payload Listing  \n' + '-' * 40)
    +    print(payloads)
    +
    +
    +def main(argv):
    +    """Command line interface."""
    +    parser = argparse.ArgumentParser()
    +    bosdyn.client.util.add_base_arguments(parser)
    +    bosdyn.client.util.add_payload_credentials_arguments(parser)
    +    group = parser.add_mutually_exclusive_group(required=True)
    +    group.add_argument('--attach', help='Attach the payload.', action='store_true')
    +    group.add_argument('--detach', help='Detach the payload.', action='store_true')
    +    options = parser.parse_args(argv)
    +
    +    payload_attach_detach(options)
    +
    +    return True
    +
    +
    +if __name__ == '__main__':
    +    if not main(sys.argv[1:]):
    +        sys.exit(1)
    diff --git a/python/examples/payloads/payloads.py b/python/examples/payloads/payloads.py
    index e7df39d48..60fcdd4b5 100644
    --- a/python/examples/payloads/payloads.py
    +++ b/python/examples/payloads/payloads.py
    @@ -56,6 +56,8 @@ def payload_spot(config):
         payload.version.major_version = 1
         payload.version.minor_version = 1
         payload.version.patch_level = 1
    +    # note: this field is not required, but highly recommended
    +    payload.mount_frame_name = payload_protos.MOUNT_FRAME_GRIPPER_PAYLOAD
     
         # Register the payload
         payload_registration_client.register_payload(payload, payload_secret)
    @@ -90,6 +92,8 @@ def payload_spot(config):
         payload.version.major_version = 3
         payload.version.minor_version = 2
         payload.version.patch_level = 1
    +    # note: this field is not required, but highly recommended
    +    payload.mount_frame_name = payload_protos.MOUNT_FRAME_GRIPPER_PAYLOAD
     
         # Create and start the keep alive
         keep_alive = PayloadRegistrationKeepAlive(payload_registration_client, payload, payload_secret)
    @@ -102,6 +106,7 @@ def payload_spot(config):
     
         print('\n\n\nDon\'t forget to clean up these registrations in the Spot payloads webpage!')
     
    +
     def main(argv):
         """Command line interface."""
         parser = argparse.ArgumentParser()
    diff --git a/python/examples/payloads/requirements.txt b/python/examples/payloads/requirements.txt
    index ccf6f8ef8..206b3c0da 100644
    --- a/python/examples/payloads/requirements.txt
    +++ b/python/examples/payloads/requirements.txt
    @@ -1,3 +1,3 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.1
    +bosdyn-client >= 3.0
    diff --git a/python/examples/post_docking_callbacks/Dockerfile b/python/examples/post_docking_callbacks/Dockerfile
    new file mode 100644
    index 000000000..7f1669189
    --- /dev/null
    +++ b/python/examples/post_docking_callbacks/Dockerfile
    @@ -0,0 +1,12 @@
    +FROM python:3.7-slim
    +
    +COPY requirements.txt .
    +
    +RUN python3 -m pip install -r requirements.txt
    +
    +COPY ./daq_upload_docking_callback.py /app/daq_upload_docking_callback.py
    +# COPY ./config ~/.aws/config
    +
    +WORKDIR /app
    +
    +ENTRYPOINT ["python3", "/app/daq_upload_docking_callback.py"]
    diff --git a/python/examples/post_docking_callbacks/README.md b/python/examples/post_docking_callbacks/README.md
    new file mode 100644
    index 000000000..849bf336d
    --- /dev/null
    +++ b/python/examples/post_docking_callbacks/README.md
    @@ -0,0 +1,81 @@
    +
    +
    +# Post Docking Callback Examples
    +
    +The scripts in this folder allow you to upload files saved to DAQ during robot operation to various endpoints, with the target use case having the callback run when Spot docks at the end of an Autowalk mission.
    +
    +## Install Packages
    +Run the below to install the necessary dependencies:
    +```
    +python3 -m pip install -r requirements.txt
    +```
    +
    +## Configuration Requirements
    +For AWS, you must have your config file saved at `~/.aws/config` with format:
    +```
    +[default]
    +aws_access_key_id=KEY
    +aws_secret_access_key=KEY
    +```
    +If running on a CORE, you will need access to the internet or your local network.
    +
    +## Run a Callback
    +Run the scripts by the following commands:
    +AWS:
    +```
    +python3 -m daq_upload_docking_callback --destination aws --bucket-name YOUR_BUCKET --host-ip HOST_COMPUTER_IP --username USERNAME --password PASSWORD SPOT_IP
    +```
    +Note: You can either use a config file at `~/.aws/config` or use the `--aws-access-key` and `--aws-secret-key` arguments to have this service create the file.
    +
    +GCP:
    +```
    +python3 -m daq_upload_docking_callback --destination gcp --key-filepath PATH_TO_KEY_JSON --bucket-name YOUR_BUCKET --host-ip HOST_COMPUTER_IP --username USERNAME --password PASSWORD SPOT_IP
    +```
    +Local:
    +```
    +python3 -m daq_upload_docking_callback --destination local --destination-folder DESTINATION --host-ip HOST_COMPUTER_IP --username USERNAME --password PASSWORD SPOT_IP
    +```
    +You can use the optional `--time-period` argument to adjust how far back the callback should look for files. If not specified, the callback will look for files starting from when the callback was initialized. After running once, the start time will be reset.
    +
    +## Run a Callback using Docker
    +
    +Please refer to this [document](../../../docs/payload/docker_containers.md) for general instructions on how to run software applications on computation payloads as docker containers.
    +
    +You can find general instructions on how to build and use the docker image [here](../../../docs/payload/docker_containers.md#build-docker-images).
    +
    +To build the Docker image, run:
    +```
    +sudo docker build -f Dockerfile  -t docking_callback .
    +sudo docker save docking_callback > docking_callback.tar
    +```
    +
    +Note: For the AWS callback, you must copy your config file as `config` to this directory for `docker build` to work. You will then uncomment `COPY ./config ~/.aws/config` in Dockerfile. Alternatively, you can supply your keys by using the `--aws-access-key` and `--aws-secret-key` arguments.
    +
    +Open Portainer (https://ROBOT_IP:21900), login, and navigate to "Images".
    +
    +Click "Import", select your docking_callback.tar file, and upload.
    +
    +Navigate to "Containers", click "Add Container", and fill out the following fields:
    +* "Name" = Name of the container. This should be set to a unique string that describes the container.
    +* "Image" = {IMAGE_NAME}:latest. {IMAGE_NAME} represents the image name used to build the docker image.
    +* "Publish all exposed network ports to random host ports" = True. This reduces port conflicts.
    +* Under the "Command & logging" tab in the container configuration page, add arguments depending on your configuration. On the CORE, the CORE_IP should be 192.168.50.5 and the ROBOT_IP should be 192.168.50.3.
    +
    +    * AWS `--destination aws --bucket-name YOUR_BUCKET --host-ip HOST_COMPUTER_IP --username USERNAME --password PASSWORD SPOT_IP`
    +    * GCP `--destination gcp --key-filepath PATH_TO_KEY_JSON --bucket-name YOUR_BUCKET --host-ip HOST_COMPUTER_IP --username USERNAME --password PASSWORD SPOT_IP`
    +    * Local `--destination local --destination-folder DESTINATION --host-ip HOST_COMPUTER_IP --username USERNAME --password PASSWORD SPOT_IP`
    +* Under the "Network" tab in the container configuration page, set the "Network" field to `host` so ports are forwarded correctly between the host OS and the docker container.
    +* Under the "Restart policy" tab in the container configuration page, set the policy to "Unless stopped". This will allow the docker container to continue to keep running all the time (even after rebooting the spot core) unless it is manually stopped by a user in Portainer.
    +
    +Click "Create". This will build your container. To verify that the callback registered, click logs and verify that you see the expected:
    +```
    +DATE TIME - INFO - Started the DaqDockingUploadServicer server.
    +DATE TIME - INFO - daq-docking-upload-callback service registered/updated.
    +DATE TIME - INFO - Starting directory registration loop for daq-docking-upload-callback
    +```
    diff --git a/python/examples/post_docking_callbacks/daq_upload_docking_callback.py b/python/examples/post_docking_callbacks/daq_upload_docking_callback.py
    new file mode 100644
    index 000000000..bb448b7b7
    --- /dev/null
    +++ b/python/examples/post_docking_callbacks/daq_upload_docking_callback.py
    @@ -0,0 +1,323 @@
    +# Copyright (c) 2021 Boston Dynamics, Inc.  All rights reserved.
    +#
    +# Downloading, reproducing, distributing or otherwise using the SDK Software
    +# is subject to the terms and conditions of the Boston Dynamics Software
    +# Development Kit License (20191101-BDSDK-SL).
    +
    +"""Docking callback to automatically upload DAQ data to a destination."""
    +
    +import argparse
    +import datetime
    +import glob
    +import logging
    +import os
    +import random
    +import requests
    +import string
    +import threading
    +import time
    +import zipfile
    +
    +import boto3
    +import botocore
    +import google.auth
    +from google.cloud import storage
    +from google.protobuf.timestamp_pb2 import Timestamp
    +
    +import bosdyn.client
    +import bosdyn.client.util
    +from bosdyn.api import data_acquisition_pb2, data_acquisition_store_pb2
    +from bosdyn.api.mission import remote_pb2, remote_service_pb2_grpc
    +from bosdyn.client.auth import AuthResponseError
    +from bosdyn.client.data_acquisition_helpers import download_data_REST, make_time_query_params
    +from bosdyn.client.directory_registration import (DirectoryRegistrationClient,
    +                                                  DirectoryRegistrationKeepAlive)
    +from bosdyn.client.server_util import GrpcServiceRunner, ResponseContext
    +from bosdyn.client.util import setup_logging
    +from bosdyn.mission import server_util, util
    +
    +DIRECTORY_NAME = 'daq-docking-upload-callback'
    +AUTHORITY = 'remote-mission'
    +SERVICE_TYPE = 'bosdyn.api.mission.RemoteMissionService'
    +
    +_LOGGER = logging.getLogger(__name__)
    +
    +
    +class Session:
    +
    +    def __init__(self, thread=None, tick_status=None):
    +        self.thread = thread
    +        self.tick_status = tick_status
    +
    +
    +class DaqDockingUploadServicer(remote_service_pb2_grpc.RemoteMissionServiceServicer):
    +    """When run, uploads all DAQ data to an S3 bucket.
    +    """
    +
    +    def __init__(self, bosdyn_sdk_robot, options, logger=None):
    +        self.logger = logger or _LOGGER
    +        self.bosdyn_sdk_robot = bosdyn_sdk_robot
    +        self.sessions_by_id = {}
    +        self._used_session_ids = []
    +        self.lock = threading.Lock()
    +        self.options = options
    +        self.destination_folder = options.destination_folder
    +        self.start_time = time.time()
    +        if self.options.destination == 'aws':
    +            self.check_for_config()
    +            self.s3_client = boto3.client('s3')
    +        elif self.options.destination == 'gcp':
    +            self.storage_client = storage.Client.from_service_account_json(options.key_filepath)
    +            self.bucket = self.storage_client.bucket(options.bucket_name)
    +
    +    def check_for_config(self):
    +        key_path = os.path.join(os.path.expanduser('~'), '.aws', 'config')
    +        if not os.path.exists(key_path):
    +            if self.options.aws_access_key is not None and self.options.aws_secret_key is not None:
    +                with open(key_path, 'w') as config_file:
    +                    config_file.writelines([
    +                        '[default]\n', 'aws_access_key_id=' + self.options.aws_access_key + '\n',
    +                        'aws_secret_access_key=' + self.options.aws_secret_key
    +                    ])
    +            else:
    +                print('Config file does not exist. Please provide AWS keys and try again.')
    +                exit()
    +
    +    def Tick(self, request, context):
    +        response = remote_pb2.TickResponse()
    +        self.logger.debug('Ticked with session ID "%s" %i leases and %i inputs', request.session_id,
    +                          len(request.leases), len(request.inputs))
    +        with ResponseContext(response, request):
    +            with self.lock:
    +                self._tick_implementation(request, response)
    +        return response
    +
    +    def _tick_implementation(self, request, response):
    +
    +        if request.session_id not in self.sessions_by_id:
    +            self.logger.error('Do not know about session ID "%s"', request.session_id)
    +            response.status = remote_pb2.TickResponse.STATUS_INVALID_SESSION_ID
    +            return
    +
    +        tick_background_thread = self.sessions_by_id[request.session_id].thread
    +        if tick_background_thread is None:
    +            tick_background_thread_start = threading.Thread(target=self.callback_code,
    +                                                            args=(request,))
    +            tick_background_thread_start.start()
    +            self.sessions_by_id[request.session_id] = Session(
    +                tick_background_thread_start, remote_pb2.TickResponse.STATUS_RUNNING)
    +            response.status = remote_pb2.TickResponse.STATUS_RUNNING
    +
    +        elif tick_background_thread.is_alive():
    +            response.status = remote_pb2.TickResponse.STATUS_RUNNING
    +        else:
    +            session_status = self.sessions_by_id[request.session_id].tick_status
    +            if (session_status == remote_pb2.TickResponse.STATUS_UNKNOWN) or (
    +                    session_status == remote_pb2.TickResponse.STATUS_RUNNING):
    +                self.logger.info('TickResponse was not updated. Default to STATUS_SUCCESS')
    +                response.status = remote_pb2.TickResponse.STATUS_SUCCESS
    +            else:
    +                response.status = session_status
    +
    +        return response
    +
    +    def callback_code(self, request):
    +        query_params = None
    +        try:
    +            current_time = time.time()
    +            query_params = make_time_query_params(self.start_time, current_time, robot)
    +        except ValueError as val_err:
    +            print("Value Exception:\n" + str(val_err))
    +
    +        retry = 0
    +        success = False
    +        while not success and retry < 10:
    +            success = download_data_REST(query_params, options.hostname, robot.user_token,
    +                                         self.destination_folder)
    +            retry += 1
    +
    +        if not success:
    +            self.logger.info('Unable to download mission data for {} through {}.'.format(
    +                self.start_time, current_time))
    +            return
    +
    +        downloaded_zip_file = max(glob.glob(os.path.join(self.destination_folder, 'REST/*')),
    +                                  key=os.path.getctime)
    +
    +        if self.options.unzip:
    +            with zipfile.ZipFile(downloaded_zip_file, 'r') as zip_file:
    +                zip_file.extractall(path=self.options.destination_folder)
    +                list_of_files = [
    +                    os.path.join(self.options.destination_folder, file)
    +                    for file in zip_file.namelist()
    +                ]
    +        else:
    +            list_of_files = [downloaded_zip_file]
    +
    +        upload = None
    +        if self.options.destination == 'local':
    +            return
    +        elif self.options.destination == 'aws':
    +            upload = self.upload_to_aws
    +        else:
    +            upload = self.upload_to_gcp
    +
    +        retry = 0
    +        start_time_string = datetime.datetime.fromtimestamp(self.start_time).strftime('%Y-%m-%d_%H:%M:%S')
    +        current_time_string = datetime.datetime.fromtimestamp(current_time).strftime('%Y-%m-%d_%H:%M:%S')
    +        while len(list_of_files) != 0 and retry < 10:
    +            list_of_files = upload(list_of_files, start_time_string, current_time_string)
    +            retry += 1
    +        if len(list_of_files) != 0:
    +            self.logger.info('Unable to upload {}'.format(list_of_files))
    +
    +        self.start_time = time.time()
    +
    +    def upload_to_aws(self, source_files, start_time, current_time):
    +        """ Uploads a list of files to your S3 bucket """
    +        failed_files = []
    +        for source_file in source_files:
    +            try:
    +                destination_file = '{}_{}/{}'.format(start_time, current_time, source_file.split('/')[-1])
    +                self.s3_client.upload_file(source_file, self.options.bucket_name, destination_file)
    +                self.logger.info('Upload of file {} as {} to {} successful'.format(
    +                    source_file, destination_file, self.options.bucket_name))
    +            except IOError:
    +                self.logger.info('The file {} was not found'.format(source_files))
    +                failed_files.append(source_file)
    +            except botocore.exceptions.EndpointConnectionError:
    +                self.logger.info(
    +                    'Could not connect to AWS. File is available at {}'.format(source_file))
    +                failed_files.append(source_file)
    +            except Exception as e:
    +                self.logger.info('Unknown exception occurred when trying to upload {}. {}'.format(
    +                    source_file, e))
    +                failed_files.append(source_file)
    +        return failed_files
    +
    +    def upload_to_gcp(self, source_files, start_time, current_time):
    +        """Uploads a list of files to the bucket."""
    +        failed_files = []
    +        for source_file in source_files:
    +            destination = '{}_{}/{}'.format(start_time, current_time, source_file.split('/')[-1])
    +            blob = self.bucket.blob(destination)
    +            try:
    +                blob.upload_from_filename(source_file)
    +                self.logger.info('Upload of file {} as {} to {} successful'.format(
    +                    source_file, source_file, self.options.bucket_name))
    +            except IOError:
    +                self.logger.info('The file {} was not found'.format(source_file))
    +                failed_files.append(source_file)
    +            except (requests.exceptions.ReadTimeout, google.auth.exceptions.TransportError):
    +                self.logger.info(
    +                    'Could not connect to GCP. File is available at {}'.format(source_file))
    +                failed_files.append(source_file)
    +            except Exception as e:
    +                self.logger.info('Unknown exception occurred when trying to upload {}. {}'.format(
    +                    source_file, e))
    +                failed_files.append(source_file)
    +        return failed_files
    +
    +    def _get_unique_random_session_id(self):
    +        """Create a random 16-character session ID that hasn't been used."""
    +        while True:
    +            session_id = ''.join([random.choice(string.ascii_letters) for _ in range(16)])
    +            if session_id not in self._used_session_ids:
    +                return session_id
    +
    +    def EstablishSession(self, request, context):
    +        response = remote_pb2.EstablishSessionResponse()
    +        with ResponseContext(response, request):
    +            response.status = remote_pb2.EstablishSessionResponse.STATUS_OK
    +        session_id = self._get_unique_random_session_id()
    +        self.sessions_by_id[session_id] = Session()
    +        self._used_session_ids.append(session_id)
    +        response.session_id = session_id
    +        if self.options.time_period:
    +            self.start_time = (
    +                datetime.datetime.utcnow() -
    +                datetime.timedelta(minutes=self.options.time_period)).strftime("%Y-%m-%dT%H:%M:%SZ")
    +        return response
    +
    +    def Stop(self, request, context):
    +        response = remote_pb2.StopResponse()
    +        with ResponseContext(response, request):
    +            self.logger.info('Stopping session.')
    +            self.sessions_by_id[request.session_id] = Session()
    +            response.status = remote_pb2.StopResponse.STATUS_OK
    +        return response
    +
    +    def TeardownSession(self, request, context):
    +        response = remote_pb2.TeardownSessionResponse()
    +        with ResponseContext(response, request):
    +            self.logger.info('Tearing down session.')
    +            if request.session_id in self.sessions_by_id:
    +                del self.sessions_by_id[request.session_id]
    +                response.status = remote_pb2.TeardownSessionResponse.STATUS_OK
    +            else:
    +                response.status = remote_pb2.TeardownSessionResponse.STATUS_INVALID_SESSION_ID
    +        return response
    +
    +
    +def run_service(bosdyn_sdk_robot, options, logger=None):
    +    # Proto service specific function used to attach a servicer to a server.
    +    add_servicer_to_server_fn = remote_service_pb2_grpc.add_RemoteMissionServiceServicer_to_server
    +
    +    # Instance of the servicer to be run.
    +    service_servicer = DaqDockingUploadServicer(bosdyn_sdk_robot, options, logger=logger)
    +    return GrpcServiceRunner(service_servicer, add_servicer_to_server_fn, options.port,
    +                             logger=logger)
    +
    +
    +if __name__ == '__main__':
    +
    +    parser = argparse.ArgumentParser()
    +    bosdyn.client.util.add_common_arguments(parser)
    +    bosdyn.client.util.add_service_endpoint_arguments(parser)
    +    parser.add_argument('--destination', default='local', choices=['aws', 'gcp', 'local'])
    +    parser.add_argument('--time-period', help=('How far back to download DAQ data in minutes.'),
    +                        required=False, type=int)
    +    parser.add_argument('--bucket-name', help=('The S3 or GCP bucket to save the acquired data.'))
    +    parser.add_argument('--aws-access-key', help=('Required if ~/.aws/config does not exist.'),
    +                        default=None, type=str)
    +    parser.add_argument('--aws-secret-key', help=('Required if ~/.aws/config does not exist.'),
    +                        default=None, type=str)
    +    parser.add_argument('--key-filepath', help=('The filepath of your GCP key.'))
    +    parser.add_argument('--destination-folder', help=('The folder to save the acquired data'),
    +                        required=False, default='/tmp')
    +    parser.add_argument('--unzip', action='store_true', help='Unzip the acquired DAQ file.')
    +
    +    options = parser.parse_args()
    +
    +    if (options.destination == 'aws' and options.bucket_name is None):
    +        print('No bucket name passed for AWS. Please specify your bucket name and try again.')
    +        exit()
    +
    +    elif (options.destination == 'gcp' and options.key_filepath is None):
    +        print('No key filepath passed for GCP. Please provide your key and try again.')
    +        exit()
    +
    +    elif (options.destination == 'gcp' and options.bucket_name is None):
    +        print('No bucket name passed for GCP. Please specify your bucket name and try again.')
    +        exit()
    +
    +    # Setup logging to use either INFO level or DEBUG level.
    +    setup_logging(options.verbose)
    +
    +    # Create and authenticate a bosdyn robot object.
    +    sdk = bosdyn.client.create_standard_sdk("DaqUploadMissionServiceSDK")
    +    robot = sdk.create_robot(options.hostname)
    +    robot.authenticate(options.username, options.password)
    +    robot.time_sync.wait_for_sync()
    +
    +    # Create a service runner to start and maintain the service on background thread.
    +    service_runner = run_service(robot, options, logger=_LOGGER)
    +
    +    # Use a keep alive to register the service with the robot directory.
    +    dir_reg_client = robot.ensure_client(DirectoryRegistrationClient.default_service_name)
    +    keep_alive = DirectoryRegistrationKeepAlive(dir_reg_client, logger=_LOGGER)
    +    keep_alive.start(DIRECTORY_NAME, SERVICE_TYPE, AUTHORITY, options.host_ip, service_runner.port)
    +
    +    # Attach the keep alive to the service runner and run until a SIGINT is received.
    +    with keep_alive:
    +        service_runner.run_until_interrupt()
    diff --git a/python/examples/post_docking_callbacks/requirements.txt b/python/examples/post_docking_callbacks/requirements.txt
    new file mode 100644
    index 000000000..34b76891a
    --- /dev/null
    +++ b/python/examples/post_docking_callbacks/requirements.txt
    @@ -0,0 +1,6 @@
    +-f ../../../prebuilt
    +bosdyn-api >= 3.0
    +bosdyn-client >= 3.0
    +bosdyn-mission >= 3.0
    +google-cloud-storage==1.26.0
    +boto3==1.12.24
    diff --git a/python/examples/remote_mission_service/build_mission.py b/python/examples/remote_mission_service/build_mission.py
    index 194655517..f211a34de 100644
    --- a/python/examples/remote_mission_service/build_mission.py
    +++ b/python/examples/remote_mission_service/build_mission.py
    @@ -19,8 +19,9 @@
     if __name__ == '__main__':
         parser = argparse.ArgumentParser()
         parser.add_argument('output_file', help='File to save the mission to.')
    -    parser.add_argument('--add-resources', help=('Resource the remote mission needs, like "body".'
    -                                                 ' Can be comma separated for multiple resources.'))
    +    parser.add_argument(
    +        '--add-resources', help=('Resource the remote mission needs, like "body".'
    +                                 ' Can be comma separated for multiple resources.'))
         parser.add_argument(
             '--user-string',
             help='Specify the user-string input to Tick. Set to the node name in Autowalk missions.')
    diff --git a/python/examples/remote_mission_service/hello_world_mission_service.py b/python/examples/remote_mission_service/hello_world_mission_service.py
    index 72939a1bd..92334e6e8 100644
    --- a/python/examples/remote_mission_service/hello_world_mission_service.py
    +++ b/python/examples/remote_mission_service/hello_world_mission_service.py
    @@ -18,8 +18,8 @@
     from bosdyn.client.lease import LeaseClient, Lease
     from bosdyn.client.robot_command import RobotCommandClient, RobotCommandBuilder
     from bosdyn.client.auth import AuthResponseError
    -from bosdyn.client.util import GrpcServiceRunner, setup_logging
    -from bosdyn.client.server_util import ResponseContext
    +from bosdyn.client.util import setup_logging
    +from bosdyn.client.server_util import ResponseContext, GrpcServiceRunner
     from bosdyn.mission import util
     
     DIRECTORY_NAME = 'hello-world-callback'
    @@ -95,7 +95,8 @@ def run_service(port, logger=None):
     
         # Create the top-level parser.
         parser = argparse.ArgumentParser()
    -    subparsers = parser.add_subparsers(help='Select how this service will be accessed.', dest='host_type')
    +    subparsers = parser.add_subparsers(help='Select how this service will be accessed.',
    +                                       dest='host_type')
     
         # Create the parser for the "local" command.
         local_parser = subparsers.add_parser('local', help='Run this example locally.')
    diff --git a/python/examples/remote_mission_service/power_off_mission_service.py b/python/examples/remote_mission_service/power_off_mission_service.py
    index 70266293e..26ecc1162 100644
    --- a/python/examples/remote_mission_service/power_off_mission_service.py
    +++ b/python/examples/remote_mission_service/power_off_mission_service.py
    @@ -22,8 +22,8 @@
     from bosdyn.client.lease import LeaseClient, Lease
     from bosdyn.client.robot_command import RobotCommandClient, RobotCommandBuilder
     from bosdyn.client.auth import AuthResponseError
    -from bosdyn.client.util import GrpcServiceRunner, setup_logging
    -from bosdyn.client.server_util import ResponseContext
    +from bosdyn.client.util import setup_logging
    +from bosdyn.client.server_util import ResponseContext, GrpcServiceRunner
     from bosdyn.mission import util
     
     DIRECTORY_NAME = 'power-off-callback'
    @@ -299,6 +299,7 @@ def _teardown_session_implementation(self, request, response):
             else:
                 response.status = remote_pb2.TeardownSessionResponse.STATUS_INVALID_SESSION_ID
     
    +
     def run_service(bosdyn_sdk_robot, port, logger=None):
         # Proto service specific function used to attach a servicer to a server.
         add_servicer_to_server_fn = remote_service_pb2_grpc.add_RemoteMissionServiceServicer_to_server
    diff --git a/python/examples/remote_mission_service/remote_mission_client.py b/python/examples/remote_mission_service/remote_mission_client.py
    index 95a7a8675..f1747bf76 100644
    --- a/python/examples/remote_mission_service/remote_mission_client.py
    +++ b/python/examples/remote_mission_service/remote_mission_client.py
    @@ -28,12 +28,14 @@ def main():
             '--user-string',
             help='Specify the user-string input to Tick. Set to the node name in Autowalk missions.')
     
    -    subparsers = parser.add_subparsers(help='Select how this service will be accessed.', dest='host_type')
    +    subparsers = parser.add_subparsers(help='Select how this service will be accessed.',
    +                                       dest='host_type')
         # Create the parser for the "local" command.
         local_parser = subparsers.add_parser('local', help='Connect to a locally hosted service.')
         bosdyn.client.util.add_service_endpoint_arguments(local_parser)
         # Create the parser for the "robot" command.
    -    robot_parser = subparsers.add_parser('robot', help='Connect to a service through the robot directory.')
    +    robot_parser = subparsers.add_parser('robot',
    +                                         help='Connect to a service through the robot directory.')
         bosdyn.client.util.add_common_arguments(robot_parser)
     
         options = parser.parse_args()
    @@ -43,7 +45,6 @@ def main():
         elif options.power_off:
             directory_name = 'power-off-callback'
     
    -
         # If attempting to communicate directly to the service.
         if options.host_type == 'local':
             # Build a client that can talk directly to the RemoteMissionService implementation.
    diff --git a/python/examples/remote_mission_service/requirements.txt b/python/examples/remote_mission_service/requirements.txt
    index 63903a69f..0f5044799 100644
    --- a/python/examples/remote_mission_service/requirements.txt
    +++ b/python/examples/remote_mission_service/requirements.txt
    @@ -1,3 +1,3 @@
     -f ../../../prebuilt
    -bosdyn-client ~= 2.1
    -bosdyn-mission ~= 2.1
    +bosdyn-client >= 2.1
    +bosdyn-mission >= 2.1
    diff --git a/python/examples/replay_mission/README.md b/python/examples/replay_mission/README.md
    index 91dd8424c..f0e0c2773 100644
    --- a/python/examples/replay_mission/README.md
    +++ b/python/examples/replay_mission/README.md
    @@ -8,7 +8,7 @@ Development Kit License (20191101-BDSDK-SL).
     
     # Replaying a Mission
     
    -This example shows how to replay a mission via the API. The user may pass a map directory and/or a mission file for Spot to play back.
    +This example shows how to replay a mission via the API. The replay_mission.py script allows the user to replay Autowalk missions using graph_nav, as well as simple missions that do not use graph_nav for navigation.
     
     This example assumes that Spot can see the fiducial from "Start" of your mission. To ensure the fiducial is in view, move Spot to a location where the initial fiducial is seen and verify that the fiducial has a purple overlay in the tablet camera view. You may then disconnect the tablet, engage the E-Stop on your current device, and replay the mission.
     
    @@ -21,7 +21,17 @@ python3 -m pip install -r requirements.txt
     
     ## Run the Example
     
    -To run the example:
    +To run an Autowalk mission using the default autogenerated mission file from the tablet:
     ```
    -python3 -m replay_mission --mission  --username USER --password PASSWORD ROBOT_IP
    +python3 -m replay_mission --username USER --password PASSWORD ROBOT_IP autowalk MAP_DIRECTORY
    +```
    +
    +To run an Autowalk mission using a customized alternate mission file:
    +```
    +python3 -m replay_mission --username USER --password PASSWORD ROBOT_IP autowalk MAP_DIRECTORY --autowalk_mission MISSION_FILE
    +```
    +
    +To run a simple non-Autowalk mission that does not use graph_nav for navigation:
    +```
    +python3 -m replay_mission --username USER --password PASSWORD ROBOT_IP simple MISSION_FILE
     ```
    diff --git a/python/examples/replay_mission/replay_mission.py b/python/examples/replay_mission/replay_mission.py
    index d4ee98a2c..a8bc503b1 100644
    --- a/python/examples/replay_mission/replay_mission.py
    +++ b/python/examples/replay_mission/replay_mission.py
    @@ -13,15 +13,21 @@
     import sys
     import time
     
    +import google.protobuf.wrappers_pb2
    +
    +from bosdyn.api import robot_state_pb2
     from bosdyn.api.graph_nav import graph_nav_pb2, map_pb2, nav_pb2
     from bosdyn.api.mission import mission_pb2
     from bosdyn.api.mission import nodes_pb2
     
    +import bosdyn.api.power_pb2 as PowerServiceProto
    +
     import bosdyn.client
     import bosdyn.client.lease
     import bosdyn.client.util
     
    -from bosdyn.client.robot_command import blocking_stand, RobotCommandClient
    +from bosdyn.client.power import PowerClient, power_on, safe_power_off
    +from bosdyn.client.robot_command import blocking_stand, RobotCommandBuilder, RobotCommandClient
     from bosdyn.client.robot_state import RobotStateClient
     
     import bosdyn.api.mission
    @@ -30,8 +36,8 @@
     import bosdyn.util
     
     
    -def main():
    -    '''Replay stored mission'''
    +def main(raw_args=None):
    +    """Replay stored mission"""
     
         body_lease = None
     
    @@ -43,10 +49,6 @@ def main():
     
         bosdyn.client.util.add_common_arguments(parser)
     
    -    # If the map directory is omitted, we assume that everything the user wants done is encoded in
    -    # the mission itself.
    -    parser.add_argument('--map_directory', nargs='?', help='Optional path to map directory')
    -    parser.add_argument('--mission', dest='mission_file', help='Optional path to mission file')
         parser.add_argument('--timeout', type=float, default=3.0, dest='timeout',
                             help='Mission client timeout (s).')
         parser.add_argument('--noloc', action='store_true', default=False, dest='noloc',
    @@ -54,29 +56,55 @@ def main():
         parser.add_argument('--disable_alternate_route_finding', action='store_true', default=False,
                             dest='disable_alternate_route_finding',
                             help='Disable creating alternate-route-finding graph structure')
    +    parser.add_argument('--disable_directed_exploration', action='store_true', default=False,
    +                        dest='disable_directed_exploration',
    +                        help='Disable directed exploration for skipped blocked branches')
     
         group = parser.add_mutually_exclusive_group()
    -
         group.add_argument('--time', type=float, default=0.0, dest='duration',
                            help='Time to repeat mission (sec)')
         group.add_argument('--static', action='store_true', default=False, dest='static_mode',
                            help='Stand, but do not run robot')
     
    -    args = parser.parse_args()
    -
    -    # Use the optional map_directory argument as a proxy for these other tasks we normally do.
    -    do_localization = (args.map_directory is not None) and (not args.noloc)
    -    do_map_load = args.map_directory is not None
    -    fail_on_question = args.map_directory is not None
    -
    -    if not args.mission_file:
    -        if not args.map_directory:
    -            raise Exception('Must specify at least one of map_directory or --mission.')
    -        args.mission_file = os.path.join(args.map_directory, 'missions', 'autogenerated')
    -
    -    print('[ REPLAYING MISSION {} : MAP {} : HOSTNAME {} ]'.format(args.mission_file,
    -                                                                   args.map_directory,
    -                                                                   args.hostname))
    +    # Subparser for mission type
    +    subparsers = parser.add_subparsers(dest='mission_type', help='Mission type')
    +    subparsers.required = True
    +
    +    # Subparser for simple mission
    +    simple_parser = subparsers.add_parser('simple', help='Simple mission (non-Autowalk)')
    +    simple_parser.add_argument('simple_mission_file', help='Mission file for non-Autowalk mission.')
    +
    +    # Subparser for Autowalk mission
    +    autowalk_parser = subparsers.add_parser('autowalk', help='Autowalk mission using graph_nav')
    +    autowalk_parser.add_argument(
    +        'map_directory',
    +        help='Directory containing graph_nav map and default Autowalk mission file.')
    +    autowalk_parser.add_argument('--autowalk_mission', dest='autowalk_mission_file',
    +                                 help='Optional alternate Autowalk mission file.')
    +
    +    args = parser.parse_args(raw_args)
    +
    +    if args.mission_type == 'simple':
    +        do_map_load = False
    +        fail_on_question = False
    +        do_localization = False
    +        mission_file = args.simple_mission_file
    +        map_directory = None
    +        print('[ REPLAYING SIMPLE MISSION {} : HOSTNAME {} ]'.format(mission_file, args.hostname))
    +    else:
    +        do_map_load = True
    +        fail_on_question = True
    +        if args.noloc:
    +            do_localization = False
    +        else:
    +            do_localization = True
    +        map_directory = args.map_directory
    +        if args.autowalk_mission_file:
    +            mission_file = args.autowalk_mission_file
    +        else:
    +            mission_file = map_directory + '/missions/autogenerated'
    +        print('[ REPLAYING AUTOWALK MISSION {} : MAP DIRECTORY {} : HOSTNAME {} ]'.format(
    +            mission_file, map_directory, args.hostname))
     
         # Initialize robot object
         robot = init_robot(args.hostname, args.username, args.password)
    @@ -89,24 +117,28 @@ def main():
             raise Exception('Lease not acquired.')
         robot.logger.info('Lease acquired: %s', str(body_lease))
     
    +    # Initialize power client
    +    robot.logger.info('Starting power client...')
    +    power_client = robot.ensure_client(PowerClient.default_service_name)
    +
    +    # Initialize other clients
    +    robot_state_client, command_client, mission_client, graph_nav_client = init_clients(
    +        robot, body_lease, mission_file, map_directory, do_map_load,
    +        args.disable_alternate_route_finding)
    +
         try:
             with bosdyn.client.lease.LeaseKeepAlive(lease_client):
     
    -            # Initialize clients
    -            robot_state_client, command_client, mission_client, graph_nav_client = init_clients(
    -                robot, body_lease, args.mission_file, args.map_directory, do_map_load,
    -                args.disable_alternate_route_finding)
    -
                 assert not robot.is_estopped(), "Robot is estopped. " \
                                                 "Please use an external E-Stop client, " \
                                                 "such as the estop SDK example, to configure E-Stop."
     
    -            # Ensure robot is powered on
    -            assert ensure_power_on(robot), 'Robot power on failed.'
    +            # Turn on power
    +            power_on(power_client)
     
                 # Stand up and wait for the perception system to stabilize
                 robot.logger.info('Commanding robot to stand...')
    -            blocking_stand(command_client, timeout_sec=10)
    +            blocking_stand(command_client, timeout_sec=20)
                 countdown(5)
                 robot.logger.info('Robot standing.')
     
    @@ -127,16 +159,18 @@ def main():
                 # Run mission
                 if not args.static_mode and not localization_error:
                     if args.duration == 0.0:
    -                    run_mission(robot, mission_client, lease_client, fail_on_question, args.timeout)
    +                    run_mission(robot, mission_client, lease_client, fail_on_question, args.timeout,
    +                                args.disable_directed_exploration)
                     else:
                         repeat_mission(robot, mission_client, lease_client, args.duration,
    -                                   fail_on_question, args.timeout)
    +                                   fail_on_question, args.timeout,
    +                                   args.disable_directed_exploration)
     
         finally:
    -        # Ensure robot is powered off
    +        # Turn off power
             lease_client.lease_wallet.advance()
             robot.logger.info('Powering off...')
    -        power_off_success = ensure_power_off(robot)
    +        safe_power_off(command_client, robot_state_client)
     
             # Return lease
             robot.logger.info('Returning lease...')
    @@ -144,7 +178,7 @@ def main():
     
     
     def init_robot(hostname, username, password):
    -    '''Initialize robot object'''
    +    """Initialize robot object"""
     
         # Initialize SDK
         sdk = bosdyn.client.create_standard_sdk('MissionReplay', [bosdyn.mission.client.MissionClient])
    @@ -163,7 +197,7 @@ def init_robot(hostname, username, password):
     
     def init_clients(robot, lease, mission_file, map_directory, do_map_load,
                      disable_alternate_route_finding):
    -    '''Initialize clients'''
    +    """Initialize clients"""
     
         if not os.path.isfile(mission_file):
             robot.logger.fatal('Unable to find mission file: {}.'.format(mission_file))
    @@ -207,7 +241,7 @@ def init_clients(robot, lease, mission_file, map_directory, do_map_load,
     
     
     def countdown(length):
    -    '''Print sleep countdown'''
    +    """Print sleep countdown"""
     
         for i in range(length, 0, -1):
             print(i, end=' ', flush=True)
    @@ -216,7 +250,7 @@ def countdown(length):
     
     
     def upload_graph_and_snapshots(robot, client, lease, path, disable_alternate_route_finding):
    -    '''Upload the graph and snapshots to the robot'''
    +    """Upload the graph and snapshots to the robot"""
     
         # Load the graph from disk.
         graph_filename = os.path.join(path, 'graph')
    @@ -236,7 +270,8 @@ def upload_graph_and_snapshots(robot, client, lease, path, disable_alternate_rou
         # Load the waypoint snapshots from disk.
         current_waypoint_snapshots = dict()
         for waypoint in current_graph.waypoints:
    -
    +        if len(waypoint.snapshot_id) == 0:
    +            continue
             snapshot_filename = os.path.join(path, 'waypoint_snapshots', waypoint.snapshot_id)
             robot.logger.info('Loading waypoint snapshot from ' + snapshot_filename)
     
    @@ -248,7 +283,8 @@ def upload_graph_and_snapshots(robot, client, lease, path, disable_alternate_rou
         # Load the edge snapshots from disk.
         current_edge_snapshots = dict()
         for edge in current_graph.edges:
    -
    +        if len(edge.snapshot_id) == 0:
    +            continue
             snapshot_filename = os.path.join(path, 'edge_snapshots', edge.snapshot_id)
             robot.logger.info('Loading edge snapshot from ' + snapshot_filename)
     
    @@ -259,7 +295,9 @@ def upload_graph_and_snapshots(robot, client, lease, path, disable_alternate_rou
     
         # Upload the graph to the robot.
         robot.logger.info('Uploading the graph and snapshots to the robot...')
    -    response = client.upload_graph(graph=current_graph, lease=lease)
    +    true_if_empty = not len(current_graph.anchoring.anchors)
    +    response = client.upload_graph(graph=current_graph, lease=lease,
    +                                   generate_new_anchoring=true_if_empty)
         robot.logger.info('Uploaded graph.')
     
         # Upload the snapshots to the robot.
    @@ -275,7 +313,7 @@ def upload_graph_and_snapshots(robot, client, lease, path, disable_alternate_rou
     
     
     def upload_mission(robot, client, filename, lease):
    -    '''Upload the mission to the robot'''
    +    """Upload the mission to the robot"""
     
         # Load the mission from disk
         robot.logger.info('Loading mission from ' + filename)
    @@ -291,8 +329,9 @@ def upload_mission(robot, client, filename, lease):
         robot.logger.info('Uploaded mission to robot.')
     
     
    -def run_mission(robot, mission_client, lease_client, fail_on_question, mission_timeout):
    -    '''Run mission once'''
    +def run_mission(robot, mission_client, lease_client, fail_on_question, mission_timeout,
    +                disable_directed_exploration):
    +    """Run mission once"""
     
         robot.logger.info('Running mission')
     
    @@ -302,13 +341,17 @@ def run_mission(robot, mission_client, lease_client, fail_on_question, mission_t
             # We optionally fail if any questions are triggered. This often indicates a problem in
             # Autowalk missions.
             if mission_state.questions and fail_on_question:
    -            robot.logger.info('Mission failed by triggering operator question.')
    +            robot.logger.info('Mission failed by triggering operator question: {}'.format(
    +                mission_state.questions))
                 return False
     
             body_lease = lease_client.lease_wallet.advance()
             local_pause_time = time.time() + mission_timeout
     
    -        mission_client.play_mission(local_pause_time, [body_lease])
    +        play_settings = mission_pb2.PlaySettings(
    +            disable_directed_exploration=disable_directed_exploration)
    +
    +        mission_client.play_mission(local_pause_time, [body_lease], play_settings)
             time.sleep(1)
     
             mission_state = mission_client.get_state()
    @@ -320,7 +363,7 @@ def run_mission(robot, mission_client, lease_client, fail_on_question, mission_t
     
     
     def restart_mission(robot, mission_client, lease_client, mission_timeout):
    -    '''Restart current mission'''
    +    """Restart current mission"""
     
         robot.logger.info('Restarting mission')
     
    @@ -333,14 +376,16 @@ def restart_mission(robot, mission_client, lease_client, mission_timeout):
         return status == mission_pb2.State.STATUS_SUCCESS
     
     
    -def repeat_mission(robot, mission_client, lease_client, total_time, fail_on_question, timeout):
    -    '''Repeat mission for period of time'''
    +def repeat_mission(robot, mission_client, lease_client, total_time, fail_on_question, timeout,
    +                   disable_directed_exploration):
    +    """Repeat mission for period of time"""
     
         robot.logger.info('Repeating mission for {} seconds.'.format(total_time))
     
         # Run first mission
         start_time = time.time()
    -    mission_success = run_mission(robot, mission_client, lease_client, fail_on_question, timeout)
    +    mission_success = run_mission(robot, mission_client, lease_client, fail_on_question, timeout,
    +                                  disable_directed_exploration)
         elapsed_time = time.time() - start_time
         robot.logger.info('Elapsed time = {} (out of {})'.format(elapsed_time, total_time))
     
    @@ -352,7 +397,7 @@ def repeat_mission(robot, mission_client, lease_client, total_time, fail_on_ques
         while elapsed_time < total_time:
             restart_mission(robot, mission_client, lease_client, mission_timeout=3)
             mission_success = run_mission(robot, mission_client, lease_client, fail_on_question,
    -                                      timeout)
    +                                      timeout, disable_directed_exploration)
     
             elapsed_time = time.time() - start_time
             robot.logger.info('Elapsed time = {} (out of {})'.format(elapsed_time, total_time))
    @@ -364,36 +409,5 @@ def repeat_mission(robot, mission_client, lease_client, total_time, fail_on_ques
         return mission_success
     
     
    -def ensure_power_off(robot):
    -    '''Ensure that robot is powered off'''
    -
    -    if robot.is_powered_on():
    -        robot.power_off(cut_immediately=False, timeout_sec=20)
    -
    -    if robot.is_powered_on():
    -        robot.logger.error('Error powering off robot.')
    -        return False
    -
    -    robot.logger.info('Robot safely powered off.')
    -    return True
    -
    -
    -def ensure_power_on(robot):
    -    '''Ensure that robot is powered on'''
    -
    -    if robot.is_powered_on():
    -        return True
    -
    -    robot.logger.info('Powering on robot...')
    -    robot.power_on(timeout_sec=20)
    -
    -    if robot.is_powered_on():
    -        robot.logger.info('Robot powered on.')
    -        return True
    -
    -    robot.logger.error('Error powering on robot.')
    -    return False
    -
    -
     if __name__ == '__main__':
         main()
    diff --git a/python/examples/replay_mission/requirements.txt b/python/examples/replay_mission/requirements.txt
    index 09d127684..52a833b1f 100644
    --- a/python/examples/replay_mission/requirements.txt
    +++ b/python/examples/replay_mission/requirements.txt
    @@ -1,5 +1,5 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.2
    +bosdyn-client >= 2.2
     
    -bosdyn-mission ~= 2.2
    +bosdyn-mission >= 2.2
    diff --git a/python/examples/ricoh_theta/README.md b/python/examples/ricoh_theta/README.md
    index 83b6425c6..64ccd2f38 100644
    --- a/python/examples/ricoh_theta/README.md
    +++ b/python/examples/ricoh_theta/README.md
    @@ -90,6 +90,10 @@ The `--theta-ssid` argument is used to pass the SSID for the Ricoh Theta camera
     
     The Ricoh Theta image service will default to only attempting to capture an image from the Ricoh Theta camera when requested with a GetImage gRPC call. To have the Ricoh Theta attempt to continuously capture images, pass the command line argument `--capture-continuously`. This will cause the image service to create a background thread and attempt to query the ricoh theta at a high rate. Note, the Ricoh Theta images are very high resolution and the stitching process to go from the fisheye image to the processed image is time consuming, so sometimes the continuous captures will drain the camera's battery quickly or overwhelm the camera processors.
     
    +The `--live-stream` argument can be provided to use a quicker image capturing method. This will create a stream that reads from the live preview results, which can then use a different, lower quality image stitching algorithm that allows a faster, live stream return of images.
    +
    +**Note:** using the arguments `--live-stream` and `--capture-continuously` are recommended for the best results when working with the ricoh theta through the tablet in teleop.
    +
     Lastly, a port number for the image service can be specified using the `--port` argument. It is possible to bypass the port argument and allow a random port number to be selected, but it is discouraged since restarts may result in unexpected changes to a service listening on the old port. This port number will be used with the host-ip (HOST_COMPUTER_IP) to fully specify where the image service is running. This port number must be open and cannot be blocked by a local firewall, otherwise the ricoh-theta image service will be unreachable from the robot and the directory registration service.
     
     ### Ricoh Theta Image Service Configuration
    diff --git a/python/examples/ricoh_theta/docker-requirements.txt b/python/examples/ricoh_theta/docker-requirements.txt
    index a77b48448..f968cf5d3 100644
    --- a/python/examples/ricoh_theta/docker-requirements.txt
    +++ b/python/examples/ricoh_theta/docker-requirements.txt
    @@ -1,6 +1,6 @@
    -bosdyn-api==2.3.0
    -bosdyn-client==2.3.0
    -bosdyn-core==2.3.0
    +bosdyn-api==3.0.0
    +bosdyn-client==3.0.0
    +bosdyn-core==3.0.0
     certifi==2020.12.5
     chardet==3.0.4
     Deprecated==1.2.11
    diff --git a/python/examples/ricoh_theta/requirements.txt b/python/examples/ricoh_theta/requirements.txt
    index 0d19503d4..f988095ad 100644
    --- a/python/examples/ricoh_theta/requirements.txt
    +++ b/python/examples/ricoh_theta/requirements.txt
    @@ -1,4 +1,4 @@
     -f ../../../prebuilt
    -bosdyn-client~=2.2
    +bosdyn-client>=2.2
     requests==2.24.0
     Pillow==6.1.0
    diff --git a/python/examples/ricoh_theta/ricoh_client_mode.py b/python/examples/ricoh_theta/ricoh_client_mode.py
    index b0d309764..f0b91e719 100644
    --- a/python/examples/ricoh_theta/ricoh_client_mode.py
    +++ b/python/examples/ricoh_theta/ricoh_client_mode.py
    @@ -22,13 +22,17 @@
     
     from ricoh_theta import Theta
     
    +
     def connect(options):
         """Uses the ricoh_theta.py script to connect to an access point"""
    -    camera = Theta(theta_ssid=options.theta_ssid, theta_pw=options.theta_password, client_mode=False, show_state_at_init=True)
    -    camera.connectToAP(ap_ssid=options.wifi_ssid, ap_pw=options.wifi_password, ap_sec=options.security)
    +    camera = Theta(theta_ssid=options.theta_ssid, theta_pw=options.theta_password,
    +                   client_mode=False, show_state_at_init=True)
    +    camera.connectToAP(ap_ssid=options.wifi_ssid, ap_pw=options.wifi_password,
    +                       ap_sec=options.security)
         if options.disable_sleep_mode:
             camera.sleepMode(enabled=False)
     
    +
     def main(argv):
         """Collects command line arguments to enable client mode"""
         parser = argparse.ArgumentParser(description=__doc__,
    @@ -47,5 +51,6 @@ def main(argv):
     
         connect(options)
     
    +
     if __name__ == '__main__':
         main(sys.argv[1:])
    diff --git a/python/examples/ricoh_theta/ricoh_theta.py b/python/examples/ricoh_theta/ricoh_theta.py
    index c581ee37a..611c251ba 100644
    --- a/python/examples/ricoh_theta/ricoh_theta.py
    +++ b/python/examples/ricoh_theta/ricoh_theta.py
    @@ -20,8 +20,8 @@
     class Theta:
         """Class for interacting with a Ricoh Theta camera"""
     
    -    def __init__(self, theta_ssid=None, theta_pw=None, client_mode=True,
    -                 static_ip="192.168.80.110", subnet_mask="255.255.255.0", default_gateway="192.168.80.1",
    +    def __init__(self, theta_ssid=None, theta_pw=None, client_mode=True, static_ip="192.168.80.110",
    +                 subnet_mask="255.255.255.0", default_gateway="192.168.80.1",
                      show_state_at_init=True):
             """Creates object instance for ricoh theta.
     
    @@ -38,7 +38,7 @@ def __init__(self, theta_ssid=None, theta_pw=None, client_mode=True,
             # Adjust the below default parameters for your specific use case.
             self.theta_ssid = theta_ssid
             # Typical default password for the ricoh theta is the SSID without the first 7 letters
    -        self.theta_pw = theta_pw or self.theta_ssid[7:] # use custom password or default
    +        self.theta_pw = theta_pw or self.theta_ssid[7:]  # use custom password or default
             self.client_ip = static_ip
             # Edit additional network settings here.
             self.client_subnet = subnet_mask
    @@ -46,9 +46,9 @@ def __init__(self, theta_ssid=None, theta_pw=None, client_mode=True,
     
             # Additional Setup
             self.setMode(client_mode)
    -        self.num_images_taken = 0 # keeps track of number of images taken since last download
    -        self.download_paths = [] # stores download paths used during mission
    -        self.cmd_id = "" # shared variable for use once image processing is complete
    +        self.num_images_taken = 0  # keeps track of number of images taken since last download
    +        self.download_paths = []  # stores download paths used during mission
    +        self.cmd_id = ""  # shared variable for use once image processing is complete
     
             if show_state_at_init:
                 # Use showState to check network http connection and show initial state of camera.
    @@ -58,7 +58,7 @@ def setMode(self, client_mode=True):
             """Sets object instance to use client vs direct mode network settings."""
             if client_mode:
                 self.baseip = self.client_ip
    -        else: # default settings for direct mode
    +        else:  # default settings for direct mode
                 self.baseip = "192.168.1.1"
             self.baseurl = "http://" + self.baseip
     
    @@ -69,13 +69,14 @@ def printj(self, res, message=None):
             pjson = json.loads(res.text)
             print(json.dumps(pjson, indent=2))
     
    -    def postf(self, ext, info, print_to_screen=True):
    +    def postf(self, ext, info, print_to_screen=True, stream=False):
             """Function to create an HTTP POST Request."""
             if print_to_screen:
                 print("Request Contents:")
                 print(json.dumps(info, indent=2))
    -        return requests.post(self.baseurl + ext, json=info,
    -                             timeout=5, auth=(HTTPDigestAuth(self.theta_ssid, self.theta_pw)))
    +        return requests.post(self.baseurl + ext, json=info, timeout=5,
    +                             auth=(HTTPDigestAuth(self.theta_ssid, self.theta_pw)),
    +                             stream=stream)
     
         def sleepMode(self, enabled=None, delay=180, print_to_screen=True):
             """Alter sleep mode."""
    @@ -109,16 +110,14 @@ def takePicture(self, print_to_screen=True):
             if not self.waitUntilImageIsProcessed(print_to_screen):
                 return
             command = "/osc/commands/execute"
    -        info = {
    -            "name": "camera.takePicture"
    -        }
    +        info = {"name": "camera.takePicture"}
             res = self.postf(command, info, print_to_screen)
             self.num_images_taken += 1
             if print_to_screen:
                 self.printj(res, "Response to takePicture:")
             json_res = res.json()
             if "error" in json_res:
    -            print("takePicture failed due to: "+json_res["error"]["message"])
    +            print("takePicture failed due to: " + json_res["error"]["message"])
                 return
             self.cmd_id = json_res["id"]
     
    @@ -147,7 +146,8 @@ def previewLastImage(self, print_to_screen=True):
     
         def downloadImage(self, image_url, directory, print_to_screen=True):
             """Download image as binary and write to file as JPG."""
    -        res = requests.get(image_url, stream=True, auth=(HTTPDigestAuth(self.theta_ssid, self.theta_pw)))
    +        res = requests.get(image_url, stream=True,
    +                           auth=(HTTPDigestAuth(self.theta_ssid, self.theta_pw)))
             filename = os.path.basename(image_url)
             saveto = os.path.join(directory, filename)
             local_file = open(saveto, 'wb')
    @@ -199,24 +199,23 @@ def getLastImage(self, wait_for_latest=False, print_to_screen=True):
                 return None, None
             main_info = json_file_res["results"]["entries"][0]
             image_url = main_info["fileUrl"]
    -        img_res = requests.get(image_url, stream=True, auth=(HTTPDigestAuth(self.theta_ssid, self.theta_pw)))
    +        img_res = requests.get(image_url, stream=True,
    +                               auth=(HTTPDigestAuth(self.theta_ssid, self.theta_pw)))
             return main_info, img_res
     
         def lastImageIsProcessed(self, print_to_screen=True):
             """Check if the last captured image is processed."""
             command = "/osc/commands/status"
    -        info = {
    -            "id": self.cmd_id
    -        }
    +        info = {"id": self.cmd_id}
             res = self.postf(command, info, print_to_screen)
             json_res = res.json()
    -        state = json_res["state"] # check if inProgress or done
    +        state = json_res["state"]  # check if inProgress or done
             return state == "done"
     
         def waitUntilImageIsProcessed(self, print_to_screen=True):
             """Checks image processing time on Ricoh Theta."""
    -        waittime = 10 # seconds, total wait time before quitting command
    -        check_status_timer = 0.25 # seconds, time between each status check
    +        waittime = 10  # seconds, total wait time before quitting command
    +        check_status_timer = 0.25  # seconds, time between each status check
             timeout = time.time() + waittime
             if self.num_images_taken > 0:
                 if print_to_screen:
    @@ -246,9 +245,9 @@ def connectToAP(self, ap_ssid=None, ap_sec="WPA/WPA2 PSK", ap_pw=None, print_to_
                     "password": ap_pw,
                     "connectionPriority": 1,
                     "ipAddressAllocation": "static",
    -                "ipAddress": self.client_ip, # edit in constructor
    -                "subnetMask": self.client_subnet, # edit in constructor
    -                "defaultGateway": self.client_default, # edit in constructor
    +                "ipAddress": self.client_ip,  # edit in constructor
    +                "subnetMask": self.client_subnet,  # edit in constructor
    +                "defaultGateway": self.client_default,  # edit in constructor
                 }
             }
             res = self.postf(command, info, print_to_screen)
    @@ -280,17 +279,13 @@ def getCaptureParameters(self, print_to_screen=True):
                 exposure = res["exposureDelay"]
             gain_exposure_tuple = (gain, exposure)
             if print_to_screen:
    -            print("Camera gain: "+str(gain_exposure_tuple[0])+" and exposure time [seconds]: "+str(gain_exposure_tuple[1]))
    +            print("Camera gain: " + str(gain_exposure_tuple[0]) + " and exposure time [seconds]: " +
    +                  str(gain_exposure_tuple[1]))
             return gain_exposure_tuple
     
         def getFileFormat(self, print_to_screen=True):
             command = "/osc/commands/execute"
    -        info = {
    -            "name": "camera.getOptions",
    -            "parameters": {
    -                "optionNames": ["fileFormat"]
    -            }
    -        }
    +        info = {"name": "camera.getOptions", "parameters": {"optionNames": ["fileFormat"]}}
             result = self.postf(command, info, print_to_screen)
             json_res = result.json()
             if not ("results" in json_res and "options" in json_res["results"]):
    @@ -298,5 +293,57 @@ def getFileFormat(self, print_to_screen=True):
                 return None
             format_res = json_res["results"]["options"]["fileFormat"]
             if print_to_screen:
    -            print("The image format is: ",format_res)
    +            print("The image format is: ", format_res)
             return format_res
    +
    +
    +    def yieldLivePreview(self, print_to_screen=True):
    +        """
    +        Returns a generator for the live preview video stream as a series of jpegs (yielding the raw bytes).
    +        The capture mode must be 'image'.
    +        Credit for jpeg decoding:
    +        https://stackoverflow.com/questions/21702477/how-to-parse-mjpeg-http-stream-from-ip-camera
    +        Reference:
    +        https://developers.theta360.com/en/docs/v2/api_reference/commands/camera._get_live_preview.html
    +        """
    +        command = '/osc/commands/execute'
    +        info = {"name": "camera.getLivePreview"}
    +        response = self.postf(command, info, print_to_screen, stream=True)
    +        if print_to_screen:
    +            print("Creating the live preview had HTTP response: %s" % response)
    +
    +        if response.status_code == 200:
    +            bytes_block=b''
    +            # Iterates over the response reading the bytes buffer in chunks of 16384 bytes.
    +            for block in response.iter_content(16384):
    +                bytes_block += block
    +
    +                # Search the current block of bytes for the jpg start and end. These are common
    +                # jpeg frame start/stop indicators for mjpeg.
    +                a = bytes_block.find(b'\xff\xd8')
    +                b = bytes_block.find(b'\xff\xd9')
    +
    +                # Found a jpg file in the bytes buffer!
    +                if a !=- 1 and b != -1:
    +                    jpg = bytes_block[a:b+2]
    +                    yield jpg
    +
    +                    # Reset the buffer to point to the next set of bytes. NOTE:we start after "b+2" so that we don't
    +                    # include the previous jpg's ending byte indicator (2 bytes long) in the updated buffer.
    +                    bytes_block = bytes_block[b+2:]
    +
    +
    +    def getLivePreviewAndWriteToDisk(self, file_name_prefix="livePreview", time_limit_seconds=10, print_to_screen=True):
    +        """
    +        Save the live preview video stream to disk as a series of jpegs.
    +        The capture mode must be 'image'.
    +        """
    +        start_time = time.time()
    +        for i, jpg in enumerate(self.yieldLivePreview(print_to_screen)):
    +            frameFileName = "%s.%04d.jpg" % (file_name_prefix, i)
    +            with open(frameFileName, 'wb') as handle:
    +                handle.write(jpg)
    +            curr_time = time.time()
    +            if (curr_time - start_time) > time_limit_seconds:
    +                # End iterating and saving to disk after the time limit has elapsed.
    +                return
    diff --git a/python/examples/ricoh_theta/ricoh_theta_image_service.py b/python/examples/ricoh_theta/ricoh_theta_image_service.py
    index e7d3cc519..2c2e31415 100644
    --- a/python/examples/ricoh_theta/ricoh_theta_image_service.py
    +++ b/python/examples/ricoh_theta/ricoh_theta_image_service.py
    @@ -19,7 +19,8 @@
     import bosdyn.client.util
     from bosdyn.client.directory_registration import (DirectoryRegistrationClient,
                                                       DirectoryRegistrationKeepAlive)
    -from bosdyn.client.util import GrpcServiceRunner, setup_logging
    +from bosdyn.client.util import setup_logging
    +from bosdyn.client.server_util import GrpcServiceRunner
     from bosdyn.client.fault import FaultClient
     from bosdyn.client.image_service_helpers import VisualImageSource, CameraBaseImageServicer, CameraInterface
     
    @@ -56,10 +57,19 @@
     
     class RicohThetaServiceHelper(CameraInterface):
     
    -    def __init__(self, theta_ssid, theta_instance, logger=None):
    +    def __init__(self, theta_ssid, theta_instance, logger=None, live_stream=False,
    +                 capture_continuously=False):
             # Setup the logger.
             self.logger = logger or _LOGGER
     
    +        # Save whether or not we are capturing continuously. If we are not, but live stream is
    +        # enabled, then we should wipe the generator and start a new stream at the latest.
    +        self.capture_continuously = capture_continuously
    +
    +        # Boolean indicating which capture method: True = fast mjpeg captures with lower quality stitching, and
    +        # False = slower jpeg captures with high quality stitching
    +        self.live_stream = live_stream
    +
             # Name of the image source that is being requested from.
             self.theta_ssid = theta_ssid
             self.image_source_name = "RicohTheta_" + theta_ssid
    @@ -73,21 +83,6 @@ def __init__(self, theta_ssid, theta_instance, logger=None):
             self.camera_gain = None
             self.camera_exposure = None
     
    -        # Request the image format (height, width) from the camera.
    -        format_json = None
    -        try:
    -            format_json = self.camera.getFileFormat(print_to_screen=False)
    -        except Exception as err:
    -            # An issue occurred getting the file format for the camera images. This is likely due
    -            # to upstream failures creating the Theta instance, which already have triggered service
    -            # faults.
    -            _LOGGER.info("Unable to set the image width/height dimensions. Error message: %s %s",
    -                         str(type(err)), str(err))
    -            pass
    -        if format_json is not None:
    -            self.cols = format_json["width"]
    -            self.rows = format_json["height"]
    -
             try:
                 self.camera_gain, self.camera_exposure = self.camera.getCaptureParameters(
                     print_to_screen=False)
    @@ -99,6 +94,53 @@ def __init__(self, theta_ssid, theta_instance, logger=None):
                              str(type(err)), str(err))
                 pass
     
    +        self.mjpeg_generator = None
    +        self._maybe_reset_mjpeg_generator()
    +
    +        # Request the image format (height, width) from the camera.
    +        if not self.live_stream:
    +            format_json = None
    +            try:
    +                format_json = self.camera.getFileFormat(print_to_screen=False)
    +            except Exception as err:
    +                # An issue occurred getting the file format for the camera images. This is likely due
    +                # to upstream failures creating the Theta instance, which already have triggered service
    +                # faults.
    +                _LOGGER.info("Unable to set the image width/height dimensions. Error message: %s %s",
    +                            str(type(err)), str(err))
    +                pass
    +            if format_json is not None:
    +                print(format_json)
    +                self.cols = format_json["width"]
    +                self.rows = format_json["height"]
    +        else:
    +            # The live stream has different dimensions then the full ricoh theta image because it
    +            # is lower resolution and doesn't have the full image processing as the regular ricoh
    +            # theta still captures. There is no easy way with the API requests to get the dimensions of
    +            # the mjpeg, so we will just take the first image and decode it to get the size.
    +            if self.mjpeg_generator is not None:
    +                image_data = next(self.mjpeg_generator)
    +                pil_image = Image.open(io.BytesIO(image_data))
    +                self.cols = pil_image.size[0]
    +                self.rows = pil_image.size[1]
    +            else:
    +                _LOGGER.info("Unable to set the image dimensions because no mjpeg generator.")
    +                pass
    +
    +    def _maybe_reset_mjpeg_generator(self):
    +        """Reset the generator which reads from the mjpeg stream.
    +
    +        This is used to help catch up the live stream generator to the latest/most recent
    +        images being viewed from the camera.
    +        """
    +        if self.live_stream:
    +            try:
    +                self.mjpeg_generator = self.camera.yieldLivePreview(print_to_screen=False)
    +            except Exception as err:
    +                _LOGGER.info("Error in creating the live preview: %s %s", str(type(err)), str(err))
    +                # Default to original capture method.
    +                self.live_stream = False
    +
         def blocking_capture(self):
             """Take an image and download the processed image to local memory from the Ricoh Theta camera.
     
    @@ -108,8 +150,19 @@ def blocking_capture(self):
             if self.camera is None:
                 raise Exception("The Ricoh Theta camera instance is not initialized.")
     
    +        if not self.capture_continuously and self.live_stream:
    +            self._maybe_reset_mjpeg_generator()
    +
             # Send the request to take a picture
             capture_time_secs = time.time()
    +
    +        # If we are in "live_stream" mode, then just return the next result of the generator that reads from
    +        # the motion jpeg live preview. .
    +        if self.live_stream and self.mjpeg_generator is not None:
    +            buffer_bytes = next(self.mjpeg_generator)
    +            return buffer_bytes, capture_time_secs
    +
    +        # Otherwise, request to take a picture (blocking) and download the image.
             self.camera.takePicture(print_to_screen=False)
             img_json, img_raw = self.camera.getLastImage(wait_for_latest=True, print_to_screen=False)
             if not (img_json and img_raw):
    @@ -125,7 +178,7 @@ def blocking_capture(self):
             # the capture was triggered.
             try:
                 # Split on the "+" and "-" to remove the timezone information from the string. Note, on python 3.6
    -            # the time zome parsing in strptime does not correctly parse a timezone with a semicolon, and the
    +            # the time zone parsing in strptime does not correctly parse a timezone with a semicolon, and the
                 # ricoh theta's output includes this. So for now, we use this string splitting to just remove the
                 # timezone entirely.
                 date_time = img_json["dateTimeZone"].split("+")[0].split("-")[0]
    @@ -137,13 +190,11 @@ def blocking_capture(self):
                 # Part of the datetime string parsing failed. Therefore just use the saved capture_time_secs.
                 pass
     
    -        return buffer_bytes, capture_time_secs
    +        # Return the result of reading the image into local memory. Apparently, the action of reading the buffer can
    +        # only be performed one time unless it is converted to a stream or copied.
    +        return buffer_bytes.read(), capture_time_secs
     
         def image_decode(self, image_data, image_proto, image_format, quality_percent):
    -        # Read the image into local memory. Apparently, the action of reading the buffer can only be performed
    -        # one time unless it is converted to a stream or copied.
    -        img_bytes_read = image_data.read()
    -
             # Ricoh Theta takes colored JPEG images, so it's pixel type is RGB.
             image_proto.pixel_format = image_pb2.Image.PIXEL_FORMAT_RGB_U8
     
    @@ -156,7 +207,7 @@ def image_decode(self, image_data, image_proto, image_format, quality_percent):
                 # Note, the returned raw bytes array from the Ricoh Theta camera is often around 8MB, so the GRPC server
                 # must be setup to have an increased message size limit. The run_ricoh_image_service script does increase
                 # the size to allow for the larger raw images.
    -            pil_image = Image.open(io.BytesIO(img_bytes_read))
    +            pil_image = Image.open(io.BytesIO(image_data))
                 compressed_byte_buffer = io.BytesIO()
                 # PIL will not do any JPEG compression if the quality is specified as 100. It effectively treats
                 # requests with quality > 95 as a request for a raw image.
    @@ -168,7 +219,7 @@ def image_decode(self, image_data, image_proto, image_format, quality_percent):
                 # it matches the output of the ricoh theta camera and is compact enough to transmit.
                 # Decode the bytes into a PIL jpeg image. This allows for the formatting to be compressed. This is then
                 # converted back into a bytes array.
    -            pil_image = Image.open(io.BytesIO(img_bytes_read))
    +            pil_image = Image.open(io.BytesIO(image_data))
                 compressed_byte_buffer = io.BytesIO()
                 checked_quality = self.default_jpeg_quality
                 if quality_percent > 0 and quality_percent <= 100:
    @@ -193,7 +244,7 @@ def image_decode(self, image_data, image_proto, image_format, quality_percent):
     
     
     def make_ricoh_theta_image_service(theta_ssid, theta_password, theta_client, robot, logger=None,
    -                                   use_background_capture_thread=False):
    +                                   use_background_capture_thread=False, live_stream=False):
         # Create an theta instance, which will perform the HTTP requests to the ricoh theta
         # camera (using the Ricoh Theta API: https://api.ricoh/docs/#ricoh-theta-api).
         theta_instance = Theta(theta_ssid=theta_ssid, theta_pw=theta_password, client_mode=theta_client,
    @@ -221,7 +272,8 @@ def make_ricoh_theta_image_service(theta_ssid, theta_password, theta_client, rob
             resp = fault_client.trigger_service_fault_async(fault)
             return False, None
     
    -    ricoh_helper = RicohThetaServiceHelper(theta_ssid, theta_instance, logger)
    +    ricoh_helper = RicohThetaServiceHelper(theta_ssid, theta_instance, logger, live_stream,
    +                                           use_background_capture_thread)
         img_src = VisualImageSource(ricoh_helper.image_source_name, ricoh_helper, ricoh_helper.rows,
                                     ricoh_helper.cols, ricoh_helper.camera_gain,
                                     ricoh_helper.camera_exposure, logger)
    @@ -236,7 +288,7 @@ def run_service(bosdyn_sdk_robot, options, logger=None):
         # Instance of the servicer to be run.
         init_success, service_servicer = make_ricoh_theta_image_service(
             options.theta_ssid, options.theta_password, options.theta_client, bosdyn_sdk_robot, logger,
    -        options.capture_continuously)
    +        options.capture_continuously, options.live_stream)
         if init_success and service_servicer is not None:
             service_runner = GrpcServiceRunner(service_servicer, add_servicer_to_server_fn,
                                                options.port, logger=logger)
    @@ -258,11 +310,15 @@ def add_ricoh_theta_arguments(parser):
             "Use a background thread to request images continuously. Otherwise, capture images only when a "
             "GetImage RPC is received.")
         parser.add_argument(
    -            '--capture-when-requested', action='store_false', dest='capture_continuously',
    -            help="Only request images from the Ricoh Theta when a GetImage RPC is received. Otherwise, use "
    -            "a background thread to request images continuously.")
    +        '--capture-when-requested', action='store_false', dest='capture_continuously', help=
    +        "Only request images from the Ricoh Theta when a GetImage RPC is received. Otherwise, use "
    +        "a background thread to request images continuously.")
    +    parser.add_argument(
    +        '--live-stream', action='store_true',
    +        help="Return images as a live stream but with less high quality image stitching.")
         parser.set_defaults(capture_continuously=False)
     
    +
     if __name__ == '__main__':
         # Define all arguments used by this service.
         import argparse
    diff --git a/python/examples/ricoh_theta/test_driver.py b/python/examples/ricoh_theta/test_driver.py
    index 3c9712f06..aea7d1402 100644
    --- a/python/examples/ricoh_theta/test_driver.py
    +++ b/python/examples/ricoh_theta/test_driver.py
    @@ -24,6 +24,13 @@
     camera = Theta(theta_ssid=SSID, client_mode=False, show_state_at_init=False)
     camera.showState()
     camera.takePicture()
    +camera.downloadLastImage()
    +
    +gen = camera.yieldLivePreview()
    +jpg = next(gen)
    +with open("ricoh_theta_live_preview.jpg",'wb') as f:
    +    f.write(jpg)
    +gen.close()
     
     # Below are some additional user functions for testing:
     # camera.setMode(client_mode=False)
    diff --git a/python/examples/self_registration/announce_service.py b/python/examples/self_registration/announce_service.py
    index dcbd824cc..74f3d91db 100644
    --- a/python/examples/self_registration/announce_service.py
    +++ b/python/examples/self_registration/announce_service.py
    @@ -11,7 +11,8 @@
     """
     import argparse
     import bosdyn.client.util
    -from bosdyn.client.util import GrpcServiceRunner, setup_logging
    +from bosdyn.client.util import setup_logging
    +from bosdyn.client.server_util import GrpcServiceRunner
     import logging
     import time
     import sys
    diff --git a/python/examples/self_registration/requirements.txt b/python/examples/self_registration/requirements.txt
    index ccf6f8ef8..e2e457ae8 100644
    --- a/python/examples/self_registration/requirements.txt
    +++ b/python/examples/self_registration/requirements.txt
    @@ -1,3 +1,3 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.1
    +bosdyn-client >= 2.1
    diff --git a/python/examples/service_faults/requirements.txt b/python/examples/service_faults/requirements.txt
    index ccf6f8ef8..e2e457ae8 100644
    --- a/python/examples/service_faults/requirements.txt
    +++ b/python/examples/service_faults/requirements.txt
    @@ -1,3 +1,3 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.1
    +bosdyn-client >= 2.1
    diff --git a/python/examples/spot_cam/README.md b/python/examples/spot_cam/README.md
    index 5e4b4c724..5d6786d9d 100644
    --- a/python/examples/spot_cam/README.md
    +++ b/python/examples/spot_cam/README.md
    @@ -71,10 +71,26 @@ seq 10 | xargs -I{} python -m command_line --username=$USERNAME --password=$PASS
     python -m command_line --username=$USERNAME --password=$PASSWORD $ROBOT_IP ptz list
     python -m command_line --username=$USERNAME --password=$PASSWORD $ROBOT_IP ptz set_position mech 0 0 1
     python -m command_line --username=$USERNAME --password=$PASSWORD $ROBOT_IP ptz get_position mech
    +python -m command_line --username=$USERNAME --password=$PASSWORD $ROBOT_IP ptz set_focus auto_focus
    +python -m command_line --username=$USERNAME --password=$PASSWORD $ROBOT_IP ptz set_focus one_shot
    +python -m command_line --username=$USERNAME --password=$PASSWORD $ROBOT_IP ptz set_focus one_shot_full_scan
    +python -m command_line --username=$USERNAME --password=$PASSWORD $ROBOT_IP ptz set_focus manual_focus --distance 10
    +python -m command_line --username=$USERNAME --password=$PASSWORD $ROBOT_IP ptz get_focus
     
     # You should see a ptz jpeg image
     python -m command_line --username=$USERNAME --password=$PASSWORD -$ROBOT_IP media_log store_retrieve ptz
     
    +# Network Service
    +# Get Spot CAM network settings
    +python -m command_line --username=$USERNAME --password=$PASSWORD $ROBOT_IP network settings
    +# Set Spot CAM network settings (examples arguments below are the default values)
    +python -m command_line --username=$USERNAME --password=$PASSWORD $ROBOT_IP network set 192.168.50.6 255.255.255.0 192.168.50.3 1500
    +
    +# Get Spot CAM ICE settings
    +python -m command_line --username=$USERNAME --password=$PASSWORD $ROBOT_IP network ice_settings
    +# Set Spot CAM ICE settings (example JSON file provided)
    +python -m command_line --username=$USERNAME --password=$PASSWORD $ROBOT_IP network set_ice ice.json
    +
     # WebRTC Service
     # Save images to .jpg files
     python -m command_line --username=$USERNAME --password=$PASSWORD $ROBOT_IP webrtc save
    diff --git a/python/examples/spot_cam/audio.py b/python/examples/spot_cam/audio.py
    index 58d493576..70cb18dfc 100644
    --- a/python/examples/spot_cam/audio.py
    +++ b/python/examples/spot_cam/audio.py
    @@ -28,6 +28,10 @@ def __init__(self, subparsers, command_dict):
                 AudioPlaySoundCommand,
                 AudioDeleteSoundCommand,
                 AudioLoadSoundCommand,
    +            AudioGetAudioCaptureChannel,
    +            AudioSetAudioCaptureChannel,
    +            AudioGetAudioCaptureGain,
    +            AudioSetAudioCaptureGain
             ])
     
     
    @@ -122,3 +126,78 @@ def _run(self, robot, options):
             with open(options.src, 'rb') as fh:
                 data = fh.read()
             robot.ensure_client(AudioClient.default_service_name).load_sound(sound, data)
    +
    +#
    +# RPCs for Spot CAM+IR Only
    +#
    +
    +class AudioGetAudioCaptureChannel(Command):
    +    """Get the current microphone channel"""
    +
    +    NAME = 'get_capture_channel'
    +
    +    def __init__(self, subparsers, command_dict):
    +        super(AudioGetAudioCaptureChannel, self).__init__(subparsers, command_dict)
    +
    +    def _run(self, robot, options):
    +        channel = robot.ensure_client(AudioClient.default_service_name).get_audio_capture_channel()
    +
    +        return channel
    +
    +class AudioSetAudioCaptureChannel(Command):
    +    """Set the microphone channel"""
    +
    +    NAME = 'set_capture_channel'
    +
    +    def __init__(self, subparsers, command_dict):
    +        super(AudioSetAudioCaptureChannel, self).__init__(subparsers, command_dict)
    +        self._parser.add_argument(
    +            'channel_name', default='internal_mic', const='internal_mic', nargs='?',
    +            choices=['internal_mic', 'external_mic'])
    +
    +    def _run(self, robot, options):
    +        if options.channel_name == 'internal_mic':
    +            channel = audio_pb2.AudioCaptureChannel.AUDIO_CHANNEL_INTERNAL_MIC
    +        else:
    +            channel = audio_pb2.AudioCaptureChannel.AUDIO_CHANNEL_EXTERNAL_MIC
    +        return robot.ensure_client(AudioClient.default_service_name).set_audio_capture_channel(channel)
    +
    +class AudioGetAudioCaptureGain(Command):
    +    """Get the current gain of the external microphone"""
    +
    +    NAME = 'get_capture_gain'
    +
    +    def __init__(self, subparsers, command_dict):
    +        super(AudioGetAudioCaptureGain, self).__init__(subparsers, command_dict)
    +        self._parser.add_argument(
    +            'channel_name', default='external_mic', const='internal_mic', nargs='?',
    +            choices=['internal_mic', 'external_mic'])
    +
    +    def _run(self, robot, options):
    +        if options.channel_name == 'internal_mic':
    +            channel = audio_pb2.AudioCaptureChannel.AUDIO_CHANNEL_INTERNAL_MIC
    +        else:
    +            channel = audio_pb2.AudioCaptureChannel.AUDIO_CHANNEL_EXTERNAL_MIC
    +        gain = robot.ensure_client(AudioClient.default_service_name).get_audio_capture_gain(channel)
    +
    +        return gain
    +
    +class AudioSetAudioCaptureGain(Command):
    +    """Adjust the gain from 0.0 to 1.0"""
    +
    +    NAME = 'set_capture_gain'
    +
    +    def __init__(self, subparsers, command_dict):
    +        super(AudioSetAudioCaptureGain, self).__init__(subparsers, command_dict)
    +        self._parser.add_argument(
    +            'channel_name', default='external_mic', const='internal_mic', nargs='?',
    +            choices=['internal_mic', 'external_mic'])
    +        self._parser.add_argument('gain', help='Gain of the CAM\'s external microphone, 0.0 to 1.0')
    +
    +    def _run(self, robot, options):
    +        gain = min(max(float(options.gain), 0.0), 1.0)
    +        if options.channel_name == 'internal_mic':
    +            channel = audio_pb2.AudioCaptureChannel.AUDIO_CHANNEL_INTERNAL_MIC
    +        else:
    +            channel = audio_pb2.AudioCaptureChannel.AUDIO_CHANNEL_EXTERNAL_MIC
    +        return robot.ensure_client(AudioClient.default_service_name).set_audio_capture_gain(channel, gain)
    diff --git a/python/examples/spot_cam/command_line.py b/python/examples/spot_cam/command_line.py
    index 209ef5511..2213eea9d 100644
    --- a/python/examples/spot_cam/command_line.py
    +++ b/python/examples/spot_cam/command_line.py
    @@ -25,6 +25,7 @@
     
     from bosdyn.client import spot_cam
     
    +
     def register_all_commands(subparsers, command_dict):
         COMMANDS = [
             AudioCommands,
    @@ -44,6 +45,7 @@ def register_all_commands(subparsers, command_dict):
         for register_command in COMMANDS:
             register_command(subparsers, command_dict)
     
    +
     def main(args=None):
         """Command-line interface for interacting with Spot CAM"""
         parser = argparse.ArgumentParser(prog='bosdyn.api.spot_cam', description=main.__doc__)
    @@ -72,5 +74,6 @@ def main(args=None):
     
         return result
     
    +
     if __name__ == '__main__':
         main()
    diff --git a/python/examples/spot_cam/compositor.py b/python/examples/spot_cam/compositor.py
    index 42ebf1878..b20287727 100644
    --- a/python/examples/spot_cam/compositor.py
    +++ b/python/examples/spot_cam/compositor.py
    @@ -16,6 +16,7 @@
     
     from utils import add_bool_arg
     
    +
     class CompositorCommands(Subcommands):
         """Commands related to the Spot CAM's Compositor service"""
     
    @@ -23,9 +24,12 @@ class CompositorCommands(Subcommands):
     
         def __init__(self, subparsers, command_dict):
             super(CompositorCommands, self).__init__(subparsers, command_dict, [
    -            CompositorSetScreenCommand, CompositorGetScreenCommand,
    -            CompositorListScreensCommand, CompositorGetVisibleCamerasCommand,
    -            CompositorGetIrColorMapCommand, CompositorSetIrColorMapCommand,
    +            CompositorSetScreenCommand,
    +            CompositorGetScreenCommand,
    +            CompositorListScreensCommand,
    +            CompositorGetVisibleCamerasCommand,
    +            CompositorGetIrColorMapCommand,
    +            CompositorSetIrColorMapCommand,
             ])
     
     
    @@ -87,6 +91,7 @@ def _run(self, robot, options):
     
             return result
     
    +
     class CompositorGetVisibleCamerasCommand(Command):
         """List currently visible windows"""
     
    @@ -100,6 +105,7 @@ def _run(self, robot, options):
     
             return result
     
    +
     class CompositorGetIrColorMapCommand(Command):
         """Get currently selected IR colormap on Spot CAM"""
     
    @@ -113,6 +119,7 @@ def _run(self, robot, options):
     
             return result
     
    +
     class CompositorSetIrColorMapCommand(Command):
         """Set IR colormap to use on Spot CAM"""
     
    @@ -120,7 +127,8 @@ class CompositorSetIrColorMapCommand(Command):
     
         def __init__(self, subparsers, command_dict):
             super(CompositorSetIrColorMapCommand, self).__init__(subparsers, command_dict)
    -        self._parser.add_argument('color', default='jet', const='jet', nargs='?', choices=['jet', 'greyscale', 'grayscale'])
    +        self._parser.add_argument('color', default='jet', const='jet', nargs='?',
    +                                  choices=['jet', 'greyscale', 'grayscale'])
             self._parser.add_argument('--min-temp', default=0.0, type=float,
                                       help='minimum temperature on the temperature scale')
             self._parser.add_argument('--max-temp', default=100.0, type=float,
    @@ -136,6 +144,7 @@ def _run(self, robot, options):
     
             return result
     
    +
     class CompositorSetIrMeterOverlayCommand(Command):
         """Set IR reticle to use on Spot CAM+IR"""
     
    @@ -143,11 +152,10 @@ class CompositorSetIrMeterOverlayCommand(Command):
     
         def __init__(self, subparsers, command_dict):
             super(CompositorSetIrMeterOverlayCommand, self).__init__(subparsers, command_dict)
    -        self._parser.add_argument('color', default='jet', const='jet', nargs='?', choices=['jet', 'greyscale', 'grayscale'])
    -        self._parser.add_argument('-x', default=0.5,
    -                                  help='horizontal coordinate of reticle')
    -        self._parser.add_argument('-y', default=0.5,
    -                                  help='vertical coordinate of reticle')
    +        self._parser.add_argument('color', default='jet', const='jet', nargs='?',
    +                                  choices=['jet', 'greyscale', 'grayscale'])
    +        self._parser.add_argument('-x', default=0.5, help='horizontal coordinate of reticle')
    +        self._parser.add_argument('-y', default=0.5, help='vertical coordinate of reticle')
             add_bool_arg(self._parser, 'enable', default=True)
     
         def _run(self, robot, options):
    diff --git a/python/examples/spot_cam/health.py b/python/examples/spot_cam/health.py
    index 886fb2cb7..c63bd612f 100644
    --- a/python/examples/spot_cam/health.py
    +++ b/python/examples/spot_cam/health.py
    @@ -14,6 +14,7 @@
     
     from bosdyn.api.spot_cam import health_pb2
     
    +
     class HealthCommands(Subcommands):
         """Commands related to the Spot CAM's Health service"""
     
    @@ -21,7 +22,8 @@ class HealthCommands(Subcommands):
     
         def __init__(self, subparsers, command_dict):
             super(HealthCommands, self).__init__(subparsers, command_dict, [
    -            HealthClearBITEventsCommand, HealthGetBITStatusCommand,
    +            HealthClearBITEventsCommand,
    +            HealthGetBITStatusCommand,
                 HealthGetTemperatureCommand,
             ])
     
    @@ -47,7 +49,8 @@ def __init__(self, subparsers, command_dict):
             super(HealthGetBITStatusCommand, self).__init__(subparsers, command_dict)
     
         def _run(self, robot, options):
    -        events, degradations = robot.ensure_client(HealthClient.default_service_name).get_bit_status()
    +        events, degradations = robot.ensure_client(
    +            HealthClient.default_service_name).get_bit_status()
     
             return events, degradations
     
    diff --git a/python/examples/spot_cam/ice.json b/python/examples/spot_cam/ice.json
    new file mode 100644
    index 000000000..4e5834dd3
    --- /dev/null
    +++ b/python/examples/spot_cam/ice.json
    @@ -0,0 +1,5 @@
    +[
    +    {"type": "STUN", "address": "192.168.50.3", "port": 3478},
    +    {"type": "STUN", "address": "192.168.50.3", "port": 3479},
    +    {"type": "STUN", "address": "172.217.192.127", "port": 19302}
    +]
    \ No newline at end of file
    diff --git a/python/examples/spot_cam/media_log.py b/python/examples/spot_cam/media_log.py
    index 351ce0b5f..77ebc5ffd 100644
    --- a/python/examples/spot_cam/media_log.py
    +++ b/python/examples/spot_cam/media_log.py
    @@ -15,12 +15,12 @@
     from bosdyn.client.command_line import (Command, Subcommands)
     
     from bosdyn.client.spot_cam.media_log import MediaLogClient
    -
     from bosdyn.api import image_pb2
     from bosdyn.api.spot_cam import logging_pb2, camera_pb2
     
     from utils import add_bool_arg
     
    +
     def write_pgm(filename, width, height, max_val, data):
         """ Helper function to supplement PIL with writing a 16-bit PGM from the IR camera
         """
    @@ -30,6 +30,7 @@ def write_pgm(filename, width, height, max_val, data):
             f.write(f'{max_val}\n'.encode('utf-8'))
             f.write(data)
     
    +
     class MediaLogCommands(Subcommands):
         """Commands related to the Spot CAM's Media Log service"""
     
    @@ -45,7 +46,6 @@ def __init__(self, subparsers, command_dict):
                 MediaLogListLogpointsCommand,
                 MediaLogRetrieveCommand,
                 MediaLogRetrieveAllCommand,
    -            MediaLogSetPassphraseCommand,
                 MediaLogStoreCommand,
                 MediaLogStoreRetrieveCommand,
                 MediaLogTagCommand,
    @@ -65,6 +65,7 @@ def _run(self, robot, options):
             lp = logging_pb2.Logpoint(name=options.name)
             robot.ensure_client(MediaLogClient.default_service_name).delete(lp)
     
    +
     class MediaLogDeleteAllCommand(Command):
         """Delete all logpoints"""
     
    @@ -95,11 +96,9 @@ def __init__(self, subparsers, command_dict):
             add_bool_arg(self._parser, 'system-stats')
     
         def _run(self, robot, options):
    -        robot.ensure_client(MediaLogClient.default_service_name).enable_debug(temp=options.temperature,
    -                                                                              humidity=options.humidity,
    -                                                                              bit=options.BIT,
    -                                                                              shock=options.shock,
    -                                                                              system_stats=options.system_stats)
    +        robot.ensure_client(MediaLogClient.default_service_name).enable_debug(
    +            temp=options.temperature, humidity=options.humidity, bit=options.BIT,
    +            shock=options.shock, system_stats=options.system_stats)
     
     
     class MediaLogGetStatusCommand(Command):
    @@ -157,6 +156,7 @@ def __init__(self, subparsers, command_dict):
             self._parser.add_argument('--dst', default=None, help='Filename of saved image')
             add_bool_arg(self._parser, 'save-as-rgb24', default=False)
             add_bool_arg(self._parser, 'stitching', default=True)
    +        add_bool_arg(self._parser, "raw-ir", default=False)
     
         def _run(self, robot, options):
             lp = logging_pb2.Logpoint(name=options.name)
    @@ -167,16 +167,15 @@ def _run(self, robot, options):
         @staticmethod
         def save_logpoint_as_image(robot, lp, options, dst_filename=None):
             """'options' need to have boolean arguments save-as-rgb24 and stitching."""
    -        ir_flag = hasattr(options, 'camera_name') and options.camera_name == 'ir'
    -        if options.stitching or ir_flag:
    +        if options.stitching and not options.raw_ir:
                 lp, img = robot.ensure_client(MediaLogClient.default_service_name).retrieve(lp)
             else:
                 lp, img = robot.ensure_client(MediaLogClient.default_service_name).retrieve_raw_data(lp)
     
             # case for 16 bit raw thermal image
             if lp.image_params.format == image_pb2.Image.PIXEL_FORMAT_GREYSCALE_U16:
    -            np_img = np.frombuffer(img, dtype=np.uint16)
    -            np_img = img.reshape((lp.image_params.height, lp.image_params.width, 1))
    +            np_img = np.frombuffer(img, dtype=np.uint16).byteswap()
    +            np_img = np_img.reshape((lp.image_params.height, lp.image_params.width, 1))
                 cv2.imwrite(f'{dst_filename}.pgm', np_img)
                 return lp
     
    @@ -188,10 +187,12 @@ def save_logpoint_as_image(robot, lp, options, dst_filename=None):
                 dst_filename = os.path.basename(src_filename)
     
             # Pano and IR both come in as JPEG from retrieve command
    -        if lp.image_params.height == 4800 or (lp.image_params.width == 640 and lp.image_params.height == 512):
    +        if lp.image_params.height == 4800 or (lp.image_params.width == 640 and
    +                                              lp.image_params.height == 512):
                 shutil.move(src_filename, '{}.jpg'.format(dst_filename))
             else:
    -            target_filename = '{}-{}x{}.rgb24'.format(dst_filename, lp.image_params.width, lp.image_params.height)
    +            target_filename = '{}-{}x{}.rgb24'.format(dst_filename, lp.image_params.width,
    +                                                      lp.image_params.height)
                 shutil.move(src_filename, target_filename)
     
                 if not options.save_as_rgb24:
    @@ -199,7 +200,8 @@ def save_logpoint_as_image(robot, lp, options, dst_filename=None):
                         data = fd.read()
     
                     mode = 'RGB'
    -                image = Image.frombuffer(mode, (lp.image_params.width, lp.image_params.height), data, 'raw', mode, 0, 1)
    +                image = Image.frombuffer(mode, (lp.image_params.width, lp.image_params.height),
    +                                         data, 'raw', mode, 0, 1)
                     image.save('{}.jpg'.format(dst_filename))
     
                     os.remove(target_filename)
    @@ -222,25 +224,13 @@ def _run(self, robot, options):
     
             detailed_lps = []
             for logpoint in logpoints:
    -            lp = MediaLogRetrieveCommand.save_logpoint_as_image(robot, logpoint, options, dst_filename=logpoint.name)
    +            lp = MediaLogRetrieveCommand.save_logpoint_as_image(robot, logpoint, options,
    +                                                                dst_filename=logpoint.name)
                 detailed_lps.append(lp)
     
             return detailed_lps
     
     
    -class MediaLogSetPassphraseCommand(Command):
    -    """Enable encryption of data"""
    -
    -    NAME = 'set_passphrase'
    -
    -    def __init__(self, subparsers, command_dict):
    -        super(MediaLogSetPassphraseCommand, self).__init__(subparsers, command_dict)
    -        self._parser.add_argument('passphrase', help='double-quoted text, an empty string disables encryption')
    -
    -    def _run(self, robot, options):
    -        robot.ensure_client(MediaLogClient.default_service_name).set_passphrase(options.passphrase)
    -
    -
     class MediaLogStoreCommand(Command):
         """Trigger snapshot"""
     
    @@ -270,6 +260,7 @@ def __init__(self, subparsers, command_dict):
             self._parser.add_argument('--dst', default=None, help='Filename of saved image')
             add_bool_arg(self._parser, 'save-as-rgb24', default=False)
             add_bool_arg(self._parser, 'stitching', default=True)
    +        add_bool_arg(self._parser, 'raw-ir', default=False)
     
         def _run(self, robot, options):
             client = robot.ensure_client(MediaLogClient.default_service_name)
    @@ -280,7 +271,8 @@ def _run(self, robot, options):
             while lp.status != logging_pb2.Logpoint.COMPLETE:
                 lp = client.get_status(lp)
     
    -        lp = MediaLogRetrieveCommand.save_logpoint_as_image(robot, lp, options, dst_filename=options.dst)
    +        lp = MediaLogRetrieveCommand.save_logpoint_as_image(robot, lp, options,
    +                                                            dst_filename=options.dst)
     
             return lp
     
    diff --git a/python/examples/spot_cam/network.py b/python/examples/spot_cam/network.py
    index 1efce8752..8a941163a 100644
    --- a/python/examples/spot_cam/network.py
    +++ b/python/examples/spot_cam/network.py
    @@ -18,16 +18,21 @@
     
     from utils import int2ip
     
    +
     class NetworkCommands(Subcommands):
         """Commands related to the Spot CAM's Network service"""
     
         NAME = 'network'
     
         def __init__(self, subparsers, command_dict):
    -        super(NetworkCommands, self).__init__(subparsers, command_dict, [
    -            NetworkGetICEConfigurationCommand,
    -            NetworkSetICEConfigurationCommand,
    -        ])
    +        super(NetworkCommands, self).__init__(
    +            subparsers,
    +            command_dict,
    +            [
    +                NetworkGetICEConfigurationCommand,
    +                NetworkSetICEConfigurationCommand,
    +            ])
    +
     
     
     
    @@ -54,7 +59,8 @@ class NetworkSetICEConfigurationCommand(Command):
     
         def __init__(self, subparsers, command_dict):
             super(NetworkSetICEConfigurationCommand, self).__init__(subparsers, command_dict)
    -        self._parser.add_argument('ice_file', metavar='ice.json', type=argparse.FileType('r'), help='IceServer(s) in JSON format')
    +        self._parser.add_argument('ice_file', metavar='ice.json', type=argparse.FileType('r'),
    +                                  help='IceServer(s) in JSON format')
     
         def _run(self, robot, options):
             with options.ice_file as handler:
    @@ -62,7 +68,8 @@ def _run(self, robot, options):
     
             ice_servers = []
             for ice in json_ice_servers:
    -            ice_servers.append(network_pb2.ICEServer(type=ice['type'],
    -                                                     address=ice['address'], port=ice['port']))
    +            ice_servers.append(
    +                network_pb2.ICEServer(type=ice['type'], address=ice['address'], port=ice['port']))
     
             robot.ensure_client(NetworkClient.default_service_name).set_ice_configuration(ice_servers)
    +
    diff --git a/python/examples/spot_cam/power.py b/python/examples/spot_cam/power.py
    index 00c894f98..65df1d786 100644
    --- a/python/examples/spot_cam/power.py
    +++ b/python/examples/spot_cam/power.py
    @@ -17,6 +17,7 @@
     
     from utils import add_bool_arg
     
    +
     class PowerCommands(Subcommands):
         """Commands related to the Spot CAM's Power service"""
     
    diff --git a/python/examples/spot_cam/ptz.py b/python/examples/spot_cam/ptz.py
    index 9dec41299..f32ec3023 100644
    --- a/python/examples/spot_cam/ptz.py
    +++ b/python/examples/spot_cam/ptz.py
    @@ -8,6 +8,8 @@
     import shutil
     import tempfile
     
    +from bisect import bisect_left
    +
     from bosdyn.client.command_line import (Command, Subcommands)
     
     from bosdyn.client.spot_cam.ptz import PtzClient
    @@ -23,7 +25,7 @@ class PtzCommands(Subcommands):
         def __init__(self, subparsers, command_dict):
             super(PtzCommands, self).__init__(subparsers, command_dict, [
                 PtzListPtzCommand, PtzGetPtzPositionCommand, PtzGetPtzVelocityCommand,
    -            PtzSetPtzPositionCommand, PtzSetPtzVelocityCommand, PtzInitializeLensCommand
    +            PtzSetPtzPositionCommand, PtzSetPtzVelocityCommand, PtzInitializeLensCommand,
             ])
     
     
    @@ -48,9 +50,9 @@ class PtzGetPtzPositionCommand(Command):
     
         def __init__(self, subparsers, command_dict):
             super(PtzGetPtzPositionCommand, self).__init__(subparsers, command_dict)
    -        self._parser.add_argument('ptz_name', default='digi', const='digi', nargs='?', choices=[
    -            'digi', 'full_digi', 'mech', 'overlay_digi', 'full_pano', 'overlay_pano'
    -        ])
    +        self._parser.add_argument(
    +            'ptz_name', default='digi', const='digi', nargs='?',
    +            choices=['digi', 'full_digi', 'mech', 'overlay_digi', 'full_pano', 'overlay_pano'])
     
         def _run(self, robot, options):
             ptz_desc = ptz_pb2.PtzDescription(name=options.ptz_name)
    @@ -66,9 +68,9 @@ class PtzGetPtzVelocityCommand(Command):
     
         def __init__(self, subparsers, command_dict):
             super(PtzGetPtzVelocityCommand, self).__init__(subparsers, command_dict)
    -        self._parser.add_argument('ptz_name', default='digi', const='digi', nargs='?', choices=[
    -            'digi', 'full_digi', 'mech', 'overlay_digi', 'full_pano', 'overlay_pano'
    -        ])
    +        self._parser.add_argument(
    +            'ptz_name', default='digi', const='digi', nargs='?',
    +            choices=['digi', 'full_digi', 'mech', 'overlay_digi', 'full_pano', 'overlay_pano'])
     
         def _run(self, robot, options):
             ptz_desc = ptz_pb2.PtzDescription(name=options.ptz_name)
    @@ -84,9 +86,9 @@ class PtzSetPtzPositionCommand(Command):
     
         def __init__(self, subparsers, command_dict):
             super(PtzSetPtzPositionCommand, self).__init__(subparsers, command_dict)
    -        self._parser.add_argument('ptz_name', default='digi', const='digi', nargs='?', choices=[
    -            'digi', 'full_digi', 'mech', 'overlay_digi', 'full_pano', 'overlay_pano'
    -        ])
    +        self._parser.add_argument(
    +            'ptz_name', default='digi', const='digi', nargs='?',
    +            choices=['digi', 'full_digi', 'mech', 'overlay_digi', 'full_pano', 'overlay_pano'])
             self._parser.add_argument('pan', help='[0, 360] Degrees', default=0.0, type=float)
             self._parser.add_argument('tilt', help='[-90, 90] Degrees', default=0.0, type=float)
             self._parser.add_argument('zoom', help='[0, 0x4000]', default=0.0, type=float)
    @@ -106,9 +108,9 @@ class PtzSetPtzVelocityCommand(Command):
     
         def __init__(self, subparsers, command_dict):
             super(PtzSetPtzVelocityCommand, self).__init__(subparsers, command_dict)
    -        self._parser.add_argument('ptz_name', default='digi', const='digi', nargs='?', choices=[
    -            'digi', 'full_digi', 'mech', 'overlay_digi', 'full_pano', 'overlay_pano'
    -        ])
    +        self._parser.add_argument(
    +            'ptz_name', default='digi', const='digi', nargs='?',
    +            choices=['digi', 'full_digi', 'mech', 'overlay_digi', 'full_pano', 'overlay_pano'])
             self._parser.add_argument('pan', help='[0, 360] Degrees', default=0.0, type=float)
             self._parser.add_argument('tilt', help='[-90, 90] Degrees', default=0.0, type=float)
             self._parser.add_argument('zoom', help='[0, 0x4000]', default=0.0, type=float)
    @@ -137,3 +139,4 @@ def _run(self, robot, options):
             resp = robot.ensure_client(PtzClient.default_service_name).initialize_lens()
     
             return resp
    +
    diff --git a/python/examples/spot_cam/requirements.txt b/python/examples/spot_cam/requirements.txt
    index eae0a569b..3fccea5fc 100644
    --- a/python/examples/spot_cam/requirements.txt
    +++ b/python/examples/spot_cam/requirements.txt
    @@ -1,6 +1,6 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.1
    +bosdyn-client >= 2.1
     
     Pillow==6.1.0
     numpy==1.18.5
    diff --git a/python/examples/spot_cam/streamquality.py b/python/examples/spot_cam/streamquality.py
    index 8d97aba12..834b753f5 100644
    --- a/python/examples/spot_cam/streamquality.py
    +++ b/python/examples/spot_cam/streamquality.py
    @@ -14,15 +14,18 @@
     
     from bosdyn.api.spot_cam import streamquality_pb2
     
    +from utils import add_bool_arg
    +
    +
     class StreamQualityCommands(Subcommands):
         """Commands related to the Spot CAM's StreamQuality service"""
     
         NAME = 'stream_quality'
     
         def __init__(self, subparsers, command_dict):
    -        super(StreamQualityCommands, self).__init__(subparsers, command_dict, [
    -            StreamQualityGetStreamParamsCommand, StreamQualitySetStreamParamsCommand
    -        ])
    +        super(StreamQualityCommands, self).__init__(
    +            subparsers, command_dict,
    +            [StreamQualityGetStreamParamsCommand, StreamQualitySetStreamParamsCommand, StreamQualityCongestionControlCommand])
     
     
     class StreamQualityGetStreamParamsCommand(Command):
    @@ -43,17 +46,39 @@ class StreamQualitySetStreamParamsCommand(Command):
         """Set image quality and postprocessing settings"""
     
         NAME = 'set'
    -    AWB_MODE = ['off', 'auto', 'incandescent', 'fluorescent', 'warm_fluorescent', 'daylight', 'cloudy', 'twilight', 'shade', 'dark']
    +    AWB_MODE = [
    +        'off', 'auto', 'incandescent', 'fluorescent', 'warm_fluorescent', 'daylight', 'cloudy',
    +        'twilight', 'shade', 'dark'
    +    ]
     
         def __init__(self, subparsers, command_dict):
             super(StreamQualitySetStreamParamsCommand, self).__init__(subparsers, command_dict)
    -        self._parser.add_argument('--target-bitrate', type=int, help='video compression (bits per second)')
    -        self._parser.add_argument('--refresh-interval', type=int, help='how often to update feed (in frames)')
    -        self._parser.add_argument('--idr-interval', type=int, help='how often to send IDR message (in frames)')
    -        self._parser.add_argument('--awb-mode', default='auto', const='auto', nargs='?', choices=StreamQualitySetStreamParamsCommand.AWB_MODE)
    +        self._parser.add_argument('--target-bitrate', type=int,
    +                                  help='video compression (bits per second)')
    +        self._parser.add_argument('--refresh-interval', type=int,
    +                                  help='how often to update feed (in frames)')
    +        self._parser.add_argument('--idr-interval', type=int,
    +                                  help='how often to send IDR message (in frames)')
    +        self._parser.add_argument('--awb-mode', default='auto', const='auto', nargs='?',
    +                                  choices=StreamQualitySetStreamParamsCommand.AWB_MODE)
     
         def _run(self, robot, options):
    -        result = robot.ensure_client(StreamQualityClient.default_service_name).set_stream_params(options.target_bitrate, options.refresh_interval, options.idr_interval,
    -                                                                                                 StreamQualitySetStreamParamsCommand.AWB_MODE.index(options.awb_mode))
    +        result = robot.ensure_client(StreamQualityClient.default_service_name).set_stream_params(
    +            options.target_bitrate, options.refresh_interval, options.idr_interval,
    +            StreamQualitySetStreamParamsCommand.AWB_MODE.index(options.awb_mode))
     
             return result
    +
    +class StreamQualityCongestionControlCommand(Command):
    +    """Get image quality and postprocessing settings"""
    +
    +    NAME = 'congestion_control'
    +
    +    def __init__(self, subparsers, command_dict):
    +        super(StreamQualityCongestionControlCommand, self).__init__(subparsers, command_dict)
    +        add_bool_arg(self._parser, 'congestion_control')
    +
    +    def _run(self, robot, options):
    +        result = robot.ensure_client(StreamQualityClient.default_service_name).enable_congestion_control(options.congestion_control)
    +
    +        return result
    \ No newline at end of file
    diff --git a/python/examples/spot_cam/utils.py b/python/examples/spot_cam/utils.py
    index 6daea43ef..30d4da19c 100644
    --- a/python/examples/spot_cam/utils.py
    +++ b/python/examples/spot_cam/utils.py
    @@ -15,16 +15,21 @@
     
     from bosdyn.api.spot_cam import logging_pb2, camera_pb2
     
    +
     def add_bool_arg(parser, name, default=False, prefix=('disable', 'enable')):
         feature_parser = parser.add_mutually_exclusive_group(required=False)
         stripped_name = name.replace('-', '_')
    -    feature_parser.add_argument('--{}-{}'.format(prefix[1], name), dest=stripped_name, action='store_true')
    -    feature_parser.add_argument('--{}-{}'.format(prefix[0], name), dest=stripped_name, action='store_false')
    +    feature_parser.add_argument('--{}-{}'.format(prefix[1], name), dest=stripped_name,
    +                                action='store_true')
    +    feature_parser.add_argument('--{}-{}'.format(prefix[0], name), dest=stripped_name,
    +                                action='store_false')
         feature_parser.set_defaults(**{stripped_name: default})
     
    +
     def ip2int(addr):
         return struct.unpack("!I", socket.inet_aton(addr))[0]
     
    +
     def int2ip(addr):
         return socket.inet_ntoa(struct.pack("!I", addr))
     
    @@ -70,9 +75,10 @@ def _run(self, robot, options):
                     q = Quat(camera.base_tfrom_sensor.rotation.w, camera.base_tfrom_sensor.rotation.x,
                              camera.base_tfrom_sensor.rotation.y, camera.base_tfrom_sensor.rotation.z)
                     R = q.to_matrix()
    -                T = [camera.base_tfrom_sensor.position.x,
    -                     camera.base_tfrom_sensor.position.y,
    -                     camera.base_tfrom_sensor.position.z]
    +                T = [
    +                    camera.base_tfrom_sensor.position.x, camera.base_tfrom_sensor.position.y,
    +                    camera.base_tfrom_sensor.position.z
    +                ]
     
                     for i in range(3):
                         for j in range(3):
    diff --git a/python/examples/spot_cam/version.py b/python/examples/spot_cam/version.py
    index a7d3791a4..f527bed85 100644
    --- a/python/examples/spot_cam/version.py
    +++ b/python/examples/spot_cam/version.py
    @@ -27,7 +27,8 @@ def __init__(self, subparsers, command_dict):
             super(VersionGetSoftwareVersionCommand, self).__init__(subparsers, command_dict)
     
         def _run(self, robot, options):
    -        response = robot.ensure_client(VersionClient.default_service_name).get_software_version_full()
    +        response = robot.ensure_client(
    +            VersionClient.default_service_name).get_software_version_full()
             version = response.version
             return 'Version {}.{}.{}\n{}'.format(version.major_version, version.minor_version,
                                                  version.patch_level, response.detail)
    diff --git a/python/examples/spot_cam/webrtc.py b/python/examples/spot_cam/webrtc.py
    index 0a95eec8c..e3baa7d58 100644
    --- a/python/examples/spot_cam/webrtc.py
    +++ b/python/examples/spot_cam/webrtc.py
    @@ -27,7 +27,9 @@
     logging.basicConfig(level=logging.DEBUG, filename='webrtc.log', filemode='a+')
     STDERR = logging.getLogger('stderr')
     
    +
     class InterceptStdErr:
    +    """Intercept all exceptions and print them to StdErr without interrupting."""
         _stderr = sys.stderr
     
         def __init__(self):
    @@ -36,18 +38,14 @@ def __init__(self):
         def write(self, data):
             STDERR.error(data)
     
    -sys.stderr = InterceptStdErr()
    -
     class WebRTCCommands(Subcommands):
         """Commands related to the Spot CAM's WebRTC service"""
     
         NAME = 'webrtc'
     
         def __init__(self, subparsers, command_dict):
    -        super(WebRTCCommands, self).__init__(subparsers, command_dict, [
    -            WebRTCSaveCommand,
    -            WebRTCRecordCommand
    -        ])
    +        super(WebRTCCommands, self).__init__(subparsers, command_dict,
    +                                             [WebRTCSaveCommand, WebRTCRecordCommand])
     
     
     class WebRTCSaveCommand(Command):
    @@ -61,8 +59,7 @@ def __init__(self, subparsers, command_dict):
                                       choices=['video'])
             self._parser.add_argument('--sdp-filename', default='h264.sdp',
                                       help='File being streamed from WebRTC server')
    -        self._parser.add_argument('--sdp-port', default=31102,
    -                                  help='SDP port of WebRTC server')
    +        self._parser.add_argument('--sdp-port', default=31102, help='SDP port of WebRTC server')
             self._parser.add_argument('--cam-ssl-cert', default=None,
                                       help="Spot CAM's client cert path to check with Spot CAM server")
             self._parser.add_argument('--dst-prefix', default='h264.sdp',
    @@ -71,15 +68,15 @@ def __init__(self, subparsers, command_dict):
                                       help='Number of images to save. 0 to stream without saving.')
     
         def _run(self, robot, options):
    +        # Suppress all exceptions and log them instead.
    +        sys.stderr = InterceptStdErr()
    +
             if not options.cam_ssl_cert:
                 options.cam_ssl_cert = False
     
             shutdown_flag = threading.Event()
    -        webrtc_thread = threading.Thread(
    -            target=start_webrtc,
    -            args=[shutdown_flag, options, process_frame],
    -            daemon=True
    -        )
    +        webrtc_thread = threading.Thread(target=start_webrtc,
    +                                         args=[shutdown_flag, options, process_frame], daemon=True)
             webrtc_thread.start()
     
             try:
    @@ -91,7 +88,7 @@ def _run(self, robot, options):
     
     
     class WebRTCRecordCommand(Command):
    -    """Save webrtc stream as video"""
    +    """Save webrtc stream as video or audio"""
     
         NAME = 'record'
     
    @@ -110,6 +107,9 @@ def __init__(self, subparsers, command_dict):
                                       help='Number of seconds to record.')
     
         def _run(self, robot, options):
    +        # Suppress all exceptions and log them instead.
    +        sys.stderr = InterceptStdErr()
    +
             if not options.cam_ssl_cert:
                 options.cam_ssl_cert = False
     
    @@ -122,18 +122,13 @@ def _run(self, robot, options):
             loop = asyncio.get_event_loop()
             loop.run_until_complete(record_webrtc(options, recorder))
     
    +
     # WebRTC must be in its own thread with its own event loop.
     async def record_webrtc(options, recorder):
         config = RTCConfiguration(iceServers=[])
    -    client = WebRTCClient(options.hostname,
    -                          options.username,
    -                          options.password,
    -                          options.sdp_port,
    -                          options.sdp_filename,
    -                          options.cam_ssl_cert,
    -                          config,
    -                          media_recorder=recorder,
    -                          recorder_type=options.track)
    +    client = WebRTCClient(options.hostname, options.username, options.password, options.sdp_port,
    +                          options.sdp_filename, options.cam_ssl_cert, config,
    +                          media_recorder=recorder, recorder_type=options.track)
         await client.start()
     
         # wait for connection to be established before recording
    @@ -151,26 +146,22 @@ async def record_webrtc(options, recorder):
             await client.pc.close()
             await recorder.stop()
     
    +
     # WebRTC must be in its own thread with its own event loop.
     def start_webrtc(shutdown_flag, options, process_func, recorder=None):
         loop = asyncio.new_event_loop()
         asyncio.set_event_loop(loop)
     
         config = RTCConfiguration(iceServers=[])
    -    client = WebRTCClient(options.hostname,
    -                          options.username,
    -                          options.password,
    -                          options.sdp_port,
    -                          options.sdp_filename,
    -                          options.cam_ssl_cert,
    -                          config,
    +    client = WebRTCClient(options.hostname, options.username, options.password, options.sdp_port,
    +                          options.sdp_filename, options.cam_ssl_cert, config,
                               media_recorder=recorder)
     
    -    asyncio.gather(client.start(),
    -                   process_func(client, options, shutdown_flag),
    +    asyncio.gather(client.start(), process_func(client, options, shutdown_flag),
                        monitor_shutdown(shutdown_flag, client))
         loop.run_forever()
     
    +
     # Frame processing occurs; otherwise it waits.
     async def process_frame(client, options, shutdown_flag):
         count = 0
    @@ -183,7 +174,7 @@ async def process_frame(client, options, shutdown_flag):
     
                 frame.to_image().save(f'{options.dst_prefix}-{count}.jpg')
                 count += 1
    -    
    +
                 if count >= options.count:
                     break
             except:
    @@ -191,6 +182,7 @@ async def process_frame(client, options, shutdown_flag):
     
         shutdown_flag.set()
     
    +
     # Flag must be monitored in a different coroutine and sleep to allow frame
     # processing to occur.
     async def monitor_shutdown(shutdown_flag, client):
    diff --git a/python/examples/spot_cam/webrtc_client.py b/python/examples/spot_cam/webrtc_client.py
    index e1e952faa..1ed2ed618 100644
    --- a/python/examples/spot_cam/webrtc_client.py
    +++ b/python/examples/spot_cam/webrtc_client.py
    @@ -14,7 +14,9 @@
         MediaStreamTrack,
     )
     
    +
     class SpotCAMMediaStreamTrack(MediaStreamTrack):
    +
         def __init__(self, track, queue):
             super().__init__()
     
    @@ -27,17 +29,11 @@ async def recv(self):
     
             return frame
     
    +
     class WebRTCClient:
    -    def __init__(self,
    -                 hostname,
    -                 username,
    -                 password,
    -                 sdp_port,
    -                 sdp_filename,
    -                 cam_ssl_cert,
    -                 rtc_config,
    -                 media_recorder=None,
    -                 recorder_type=None):
    +
    +    def __init__(self, hostname, username, password, sdp_port, sdp_filename, cam_ssl_cert,
    +                 rtc_config, media_recorder=None, recorder_type=None):
             self.pc = RTCPeerConnection(configuration=rtc_config)
     
             self.video_frame_queue = asyncio.Queue()
    @@ -52,12 +48,12 @@ def __init__(self,
             self.sdp_filename = sdp_filename
             self.cam_ssl_cert = cam_ssl_cert
     
    -
    -    def get_bearer_token(self):
    +    def get_bearer_token(self, mock=False):
    +        if mock:
    +            return 'token'
             payload = {'username': self.username, 'password': self.password}
    -        response = requests.post(f'https://{self.hostname}/accounts/jwt/generate/',
    -                                 verify=False,
    -                                 data=payload)
    +        response = requests.post(f'https://{self.hostname}/accounts/jwt/generate/', verify=False,
    +                                 data=payload, timeout=1)
             token = response.content.decode('utf-8')
             return token
     
    @@ -81,7 +77,10 @@ def send_sdp_answer_to_spot_cam(self, token, offer_id, sdp_answer):
     
         async def start(self):
             # first get a token
    -        token = self.get_bearer_token()
    +        try:
    +            token = self.get_bearer_token()
    +        except:
    +            token = self.get_bearer_token(mock=True)
     
             offer_id, sdp_offer = self.get_sdp_offer_from_spot_cam(token)
     
    @@ -102,7 +101,8 @@ async def _on_ice_connection_state_change():
                 print(f'ICE connection state changed to: {self.pc.iceConnectionState}')
     
                 if self.pc.iceConnectionState == 'checking':
    -                self.send_sdp_answer_to_spot_cam(token, offer_id, self.pc.localDescription.sdp.encode())
    +                self.send_sdp_answer_to_spot_cam(token, offer_id,
    +                                                 self.pc.localDescription.sdp.encode())
     
             @self.pc.on('track')
             def _on_track(track):
    diff --git a/python/examples/spot_detect_and_follow/Dockerfile b/python/examples/spot_detect_and_follow/Dockerfile
    index 32b0ea774..1a063c430 100644
    --- a/python/examples/spot_detect_and_follow/Dockerfile
    +++ b/python/examples/spot_detect_and_follow/Dockerfile
    @@ -2,9 +2,9 @@ FROM tensorflow/tensorflow:2.1.1-gpu
     
     RUN apt-get update && apt-get install -y libsm6 libxext6 libxrender-dev libgl1-mesa-glx
     
    -COPY requirements.txt .
    +COPY docker-requirements.txt .
     
    -RUN python3 -m pip install -r requirements.txt
    +RUN python3 -m pip install -r docker-requirements.txt
     
     ENV LANG en_US.UTF-8 
     
    diff --git a/python/examples/spot_detect_and_follow/README.md b/python/examples/spot_detect_and_follow/README.md
    index aeefe825f..8928d55fc 100644
    --- a/python/examples/spot_detect_and_follow/README.md
    +++ b/python/examples/spot_detect_and_follow/README.md
    @@ -17,6 +17,10 @@ The program is organized as three sets of Python processes communicating with th
     Process Diagram
     
     ## User Guide
    +
    +This example depends on a Tensorflow model. As an example, the `faster_rcnn_inception_v2_coco` Tensorflow model pre-trained on COCO dataset can be obtained [here](http://download.tensorflow.org/models/object_detection/faster_rcnn_inception_v2_coco_2018_01_28.tar.gz). That model is not supported on Windows or MacOS.
    +The pre-trained models may not be good at detecting some classes when using the robot's cameras, as the fisheye distortion, low resolution, and black and white images affect image quality. For example, pre-trained models may not perform well at detecting "sports balls" due to the lack of color. The [ssd_mobilenet_v2_coco](http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v2_coco_2018_03_29.tar.gz) and [faster_rcnn_inception_v2_coco](http://download.tensorflow.org/models/object_detection/faster_rcnn_inception_v2_coco_2018_01_28.tar.gz) have been tested to work well at detecting humans.
    +
     ### Installation (Only if you want to run without Docker)
     To install this example on Ubuntu 18.04, follow these instructions:
     - Install python3: `sudo apt-get install python3.6`
    @@ -46,9 +50,12 @@ On top of those arguments, it also needs the following arguments:
     - --sleep-between-capture (optional) argument that specifies the amount to sleep in seconds between each image capture iteration. Increasing the value of this argument reduces the size of the queues, but also reduces the rate of command updates at the end of the pipeline; defaults to 1.0.
     - --max-processing-delay (optional) argument that specifies max delay in seconds allowed for each image before being processed; images with greater latency will not be processed; defaults to 7.0.
     
    +The simple command to run with the default values is:
     ```
    -python3 spot_detect_and_follow.py --username USER --password PASSWORD --model-path  --detection-class  ROBOT_IP
    +python3 spot_detect_and_follow.py --username USER --password PASSWORD --model-path  --detection-class  ROBOT_IP
     ```
    +For example, run the example with `--model-path` pointing to the `frozen_inference_graph.pb` file in the `faster_rcnn_inception_v2_coco_2018_01_28` folder created from untar-ing the model file downloaded from the instructions above, and with `--detection-classes` argument set to `1` to detect people in the camera images.
    +
     
     #### Running in Docker
     Please refer to this [document](../../../docs/payload/docker_containers.md) for general instructions on how to run software applications on SpotCORE as docker containers.
    @@ -59,22 +66,19 @@ Nvidia Docker is preinstalled on the Spot CORE AI.
     
     To build the image, you'll need to first copy over the tensorflow detector file first:
     ```sh
    -cp /path/to/examples/spot_tensorflow_detector/tensorflow_object_detector.py .
    +cp ../spot_tensorflow_detector/tensorflow_object_detection.py .
     sudo docker build -t spot_detect_and_follow .
     ```
     
    -To run a container:
    +To run a container, replace ROBOT_HOSTNAME and  with the full path to the pb model file in the command below:
     ```sh
    -sudo docker run --gpus all -it \
    +sudo docker run -it \
     --network=host \
     -v :/model.pb \
    --v :/classes.json \
     spot_detect_and_follow \
     --model-path /model.pb \
     --detection-class 1
    -
     ROBOT_HOSTNAME
     ```
     
    -As an example, the `faster_rcnn_inception_v2_coco` Tensorflow model pre-trained on COCO dataset can be obtained [here](http://download.tensorflow.org/models/object_detection/faster_rcnn_inception_v2_coco_2018_01_28.tar.gz). Run the example with `--model-path` pointing to the `pb` file in that model and with `--detection-classes` argument set to `1` to detect people in the camera images. That model is not supported on Windows or MacOS.
    -The pre-trained models may not be good at detecting some classes when using the robot's cameras, as the fisheye distortion, low resolution, and black and white images affect image quality. For example, pre-trained models may not perform well at detecting "sports balls" due to the lack of color. The [ssd_mobilenet_v2_coco](http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v2_coco_2018_03_29.tar.gz) and [faster_rcnn_inception_v2_coco](http://download.tensorflow.org/models/object_detection/faster_rcnn_inception_v2_coco_2018_01_28.tar.gz) have been tested to work well at detecting humans.
    \ No newline at end of file
    +To take advantage of GPU support if it is available on your system, add `--gpus all` in the command above.
    \ No newline at end of file
    diff --git a/python/examples/spot_detect_and_follow/docker-requirements.txt b/python/examples/spot_detect_and_follow/docker-requirements.txt
    index ed5dbb6b5..ab86dfb00 100644
    --- a/python/examples/spot_detect_and_follow/docker-requirements.txt
    +++ b/python/examples/spot_detect_and_follow/docker-requirements.txt
    @@ -1,15 +1,15 @@
    -bosdyn-api==2.3.0
    -bosdyn-client==2.3.0
    -bosdyn-core==2.3.0
    +bosdyn-api==3.0.0
    +bosdyn-client==3.0.0
    +bosdyn-core==3.0.0
     certifi==2020.12.5
     chardet==4.0.0
     Deprecated==1.2.10
    -grpcio==1.19.0
    +grpcio==1.24.3
     idna==2.10
     numpy==1.19.5
     opencv-python==4.4.0.46
     Pillow==6.1.0
    -protobuf==3.7.1
    +protobuf==3.8.0
     PyJWT==2.0.1
     requests==2.25.1
     scipy==1.4.1
    diff --git a/python/examples/spot_detect_and_follow/requirements.txt b/python/examples/spot_detect_and_follow/requirements.txt
    index 423aeedb0..d7e052530 100644
    --- a/python/examples/spot_detect_and_follow/requirements.txt
    +++ b/python/examples/spot_detect_and_follow/requirements.txt
    @@ -1,6 +1,6 @@
     -f ../../../prebuilt
     
    -bosdyn-client~=2.0
    +bosdyn-client>=2.0
     opencv-python~=4.4.0
     Pillow==6.1.0
     scipy~=1.4.1
    diff --git a/python/examples/spot_detect_and_follow/spot_detect_and_follow.py b/python/examples/spot_detect_and_follow/spot_detect_and_follow.py
    index e3aaad28b..621106ea3 100644
    --- a/python/examples/spot_detect_and_follow/spot_detect_and_follow.py
    +++ b/python/examples/spot_detect_and_follow/spot_detect_and_follow.py
    @@ -36,8 +36,8 @@
     from bosdyn.client.image import ImageClient
     from bosdyn.client.lease import LeaseClient, LeaseKeepAlive
     from bosdyn.client.math_helpers import Quat, SE3Pose
    -from bosdyn.client.robot_command import (RobotCommandBuilder, RobotCommandClient,
    -                                        blocking_stand, CommandFailedError, CommandTimedOutError)
    +from bosdyn.client.robot_command import (RobotCommandBuilder, RobotCommandClient, blocking_stand,
    +                                         CommandFailedError, CommandTimedOutError)
     from bosdyn.client.robot_state import RobotStateClient
     from tensorflow_object_detection import DetectorAPI
     
    @@ -73,25 +73,94 @@
     # Barrier for waiting on Tensorflow processes to start, initialized in main()
     TENSORFLOW_PROCESS_BARRIER = None
     
    -COCO_CLASS_DICT = {1:'person',2:'bicycle',3:'car',4:'motorcycle',5:'airplane',6:'bus',7:'train',
    -                   8:'truck',9:'boat',10:'trafficlight',11:'firehydrant',13:'stopsign',
    -                   14:'parkingmeter',15:'bench',16:'bird',17:'cat',18:'dog',19:'horse',20:'sheep',
    -                   21:'cow',22:'elephant',23:'bear',24:'zebra',25:'giraffe',27:'backpack',
    -                   28:'umbrella',31:'handbag',32:'tie',33:'suitcase',34:'frisbee',35:'skis',
    -                   36:'snowboard',37:'sportsball',38:'kite',39:'baseballbat',40:'baseballglove',
    -                   41:'skateboard',42:'surfboard',43:'tennisracket',44:'bottle',46:'wineglass',
    -                   47:'cup',48:'fork',49:'knife',50:'spoon',51:'bowl',52:'banana',53:'apple',
    -                   54:'sandwich',55:'orange',56:'broccoli',57:'carrot',58:'hotdog',59:'pizza',
    -                   60:'donut',61:'cake',62:'chair',63:'couch',64:'pottedplant',65:'bed',
    -                   67:'diningtable',70:'toilet',72:'tv',73:'laptop',74:'mouse',75:'remote',
    -                   76:'keyboard',77:'cellphone',78:'microwave',79:'oven',80:'toaster',81:'sink',
    -                   82:'refrigerator',84:'book',85:'clock',86:'vase',87:'scissors',88:'teddybear',
    -                   89:'hairdrier',90:'toothbrush'}
    +COCO_CLASS_DICT = {
    +    1: 'person',
    +    2: 'bicycle',
    +    3: 'car',
    +    4: 'motorcycle',
    +    5: 'airplane',
    +    6: 'bus',
    +    7: 'train',
    +    8: 'truck',
    +    9: 'boat',
    +    10: 'trafficlight',
    +    11: 'firehydrant',
    +    13: 'stopsign',
    +    14: 'parkingmeter',
    +    15: 'bench',
    +    16: 'bird',
    +    17: 'cat',
    +    18: 'dog',
    +    19: 'horse',
    +    20: 'sheep',
    +    21: 'cow',
    +    22: 'elephant',
    +    23: 'bear',
    +    24: 'zebra',
    +    25: 'giraffe',
    +    27: 'backpack',
    +    28: 'umbrella',
    +    31: 'handbag',
    +    32: 'tie',
    +    33: 'suitcase',
    +    34: 'frisbee',
    +    35: 'skis',
    +    36: 'snowboard',
    +    37: 'sportsball',
    +    38: 'kite',
    +    39: 'baseballbat',
    +    40: 'baseballglove',
    +    41: 'skateboard',
    +    42: 'surfboard',
    +    43: 'tennisracket',
    +    44: 'bottle',
    +    46: 'wineglass',
    +    47: 'cup',
    +    48: 'fork',
    +    49: 'knife',
    +    50: 'spoon',
    +    51: 'bowl',
    +    52: 'banana',
    +    53: 'apple',
    +    54: 'sandwich',
    +    55: 'orange',
    +    56: 'broccoli',
    +    57: 'carrot',
    +    58: 'hotdog',
    +    59: 'pizza',
    +    60: 'donut',
    +    61: 'cake',
    +    62: 'chair',
    +    63: 'couch',
    +    64: 'pottedplant',
    +    65: 'bed',
    +    67: 'diningtable',
    +    70: 'toilet',
    +    72: 'tv',
    +    73: 'laptop',
    +    74: 'mouse',
    +    75: 'remote',
    +    76: 'keyboard',
    +    77: 'cellphone',
    +    78: 'microwave',
    +    79: 'oven',
    +    80: 'toaster',
    +    81: 'sink',
    +    82: 'refrigerator',
    +    84: 'book',
    +    85: 'clock',
    +    86: 'vase',
    +    87: 'scissors',
    +    88: 'teddybear',
    +    89: 'hairdrier',
    +    90: 'toothbrush'
    +}
     
     # Mapping from visual to depth data
     VISUAL_SOURCE_TO_DEPTH_MAP_SOURCE = {
         'frontleft_fisheye_image': 'frontleft_depth_in_visual_frame',
    -    'frontright_fisheye_image': 'frontright_depth_in_visual_frame'}
    +    'frontright_fisheye_image': 'frontright_depth_in_visual_frame'
    +}
     ROTATION_ANGLES = {
         'back_fisheye_image': 0,
         'frontleft_fisheye_image': -78,
    @@ -100,23 +169,25 @@
         'right_fisheye_image': 180
     }
     
    +
     def _update_thread(async_task):
         while True:
             async_task.update()
             time.sleep(0.01)
     
    +
     class AsyncImage(AsyncPeriodicQuery):
         """Grab image."""
     
         def __init__(self, image_client, image_sources):
             # Period is set to be about 15 FPS
    -        super(AsyncImage, self).__init__('images', image_client, LOGGER,
    -                                         period_sec=0.067)
    +        super(AsyncImage, self).__init__('images', image_client, LOGGER, period_sec=0.067)
             self.image_sources = image_sources
     
         def _start_query(self):
             return self._client.get_image_from_sources_async(self.image_sources)
     
    +
     class AsyncRobotState(AsyncPeriodicQuery):
         """Grab robot state."""
     
    @@ -128,6 +199,7 @@ def __init__(self, robot_state_client):
         def _start_query(self):
             return self._client.get_robot_state_async()
     
    +
     def get_source_list(image_client):
         """Gets a list of image sources and filters based on config dictionary
     
    @@ -146,6 +218,7 @@ def get_source_list(image_client):
                     source_list.append(VISUAL_SOURCE_TO_DEPTH_MAP_SOURCE[source.name])
         return source_list
     
    +
     def capture_images(image_task, sleep_between_capture):
         """ Captures images and places them on the queue
     
    @@ -157,8 +230,11 @@ def capture_images(image_task, sleep_between_capture):
             images_response = image_task.proto
             if not images_response:
                 continue
    -        depth_responses = {img.source.name: img for img in images_response
    -                           if img.source.image_type == ImageSource.IMAGE_TYPE_DEPTH}
    +        depth_responses = {
    +            img.source.name: img
    +            for img in images_response
    +            if img.source.image_type == ImageSource.IMAGE_TYPE_DEPTH
    +        }
             entry = {}
             for image_response in images_response:
                 if image_response.source.image_type == ImageSource.IMAGE_TYPE_VISUAL:
    @@ -178,9 +254,7 @@ def capture_images(image_task, sleep_between_capture):
                         image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)  # Converted to RGB for TF
                         tform_snapshot = image_response.shot.transforms_snapshot
                         frame_name = image_response.shot.frame_name_image_sensor
    -                    world_tform_cam = get_a_tform_b(tform_snapshot,
    -                                                    VISION_FRAME_NAME,
    -                                                    frame_name)
    +                    world_tform_cam = get_a_tform_b(tform_snapshot, VISION_FRAME_NAME, frame_name)
                         entry[source] = {
                             'source': source,
                             'world_tform_cam': world_tform_cam,
    @@ -191,7 +265,7 @@ def capture_images(image_task, sleep_between_capture):
                             'depth_image': depth_image,
                             'system_cap_time': time.time()
                         }
    -                except Exception as exc: # pylint: disable=broad-except
    +                except Exception as exc:  # pylint: disable=broad-except
                         print(f'Exception occurred during image capture {exc}')
             try:
                 RAW_IMAGES_QUEUE.put_nowait(entry)
    @@ -199,6 +273,7 @@ def capture_images(image_task, sleep_between_capture):
                 print(f'RAW_IMAGES_QUEUE is full: {exc}')
             time.sleep(sleep_between_capture)
     
    +
     def start_tensorflow_processes(num_processes, model_path, detection_class, detection_threshold,
                                    max_processing_delay):
         """Starts Tensorflow processes in parallel.
    @@ -215,12 +290,13 @@ def start_tensorflow_processes(num_processes, model_path, detection_class, detec
         """
     
         for counter in range(num_processes):
    -        process = Process(target=process_images, args=(
    -            model_path,
    -            detection_class,
    -            detection_threshold,
    -            max_processing_delay,
    -        ))
    +        process = Process(
    +            target=process_images, args=(
    +                model_path,
    +                detection_class,
    +                detection_threshold,
    +                max_processing_delay,
    +            ))
             process.start()
     
     
    @@ -262,8 +338,7 @@ def process_images(model_path, detection_class, detection_threshold, max_process
                 confident_scores = []
                 if len(boxes) == 0:
                     continue
    -            for box, score, box_class in sorted(zip(boxes, scores, classes),
    -                                                key=lambda x: x[1],
    +            for box, score, box_class in sorted(zip(boxes, scores, classes), key=lambda x: x[1],
                                                     reverse=True):
                     # if score > detection_threshold:
                     if score > detection_threshold and box_class == detection_class:
    @@ -282,6 +357,7 @@ def process_images(model_path, detection_class, detection_threshold, max_process
             except Full as exc:
                 print(f'PROCESSED_BOXES_QUEUE is full: {exc}')
     
    +
     def get_go_to(world_tform_object, robot_state, mobility_params, dist_margin=1.2):
         """Gets trajectory command to a goal location
     
    @@ -303,11 +379,13 @@ def get_go_to(world_tform_object, robot_state, mobility_params, dist_margin=1.2)
             world_tform_object.x - delta_ewrt_vo_norm[0] * dist_margin,
             world_tform_object.y - delta_ewrt_vo_norm[1] * dist_margin
         ])
    -    tag_cmd = RobotCommandBuilder.trajectory_command(
    -                goal_x=vo_tform_goal[0], goal_y=vo_tform_goal[1], goal_heading=heading,
    -                frame_name=VISION_FRAME_NAME, params=mobility_params)
    +    tag_cmd = RobotCommandBuilder.trajectory_command(goal_x=vo_tform_goal[0],
    +                                                     goal_y=vo_tform_goal[1], goal_heading=heading,
    +                                                     frame_name=VISION_FRAME_NAME,
    +                                                     params=mobility_params)
         return tag_cmd
     
    +
     def _get_heading(xhat):
         zhat = [0.0, 0.0, 1.0]
         yhat = np.cross(zhat, xhat)
    @@ -337,10 +415,9 @@ def get_mobility_params():
                                                           locomotion_hint=spot_command_pb2.HINT_TROT)
         return mobility_params
     
    -def get_distance_to_closest_object_depth(x_min, x_max, y_min, y_max,
    -                                         depth_scale, raw_depth_image,
    -                                         histogram_bin_size=0.20,
    -                                         minimum_number_of_points=100,
    +
    +def get_distance_to_closest_object_depth(x_min, x_max, y_min, y_max, depth_scale, raw_depth_image,
    +                                         histogram_bin_size=0.20, minimum_number_of_points=100,
                                              max_distance=4.0):
         """Make a histogram of distances to points in the cloud and take the closest distance with
         enough points.
    @@ -367,7 +444,7 @@ def get_distance_to_closest_object_depth(x_min, x_max, y_min, y_max,
                     depth = raw_depth / depth_scale
                     depths.append(depth)
     
    -    hist, hist_edges = np.histogram(depths, bins=num_bins, range=(0,max_distance))
    +    hist, hist_edges = np.histogram(depths, bins=num_bins, range=(0, max_distance))
     
         edges_zipped = zip(hist_edges[:-1], hist_edges[1:])
         # Iterate over the histogram and return the first distance with enough points.
    @@ -377,6 +454,7 @@ def get_distance_to_closest_object_depth(x_min, x_max, y_min, y_max,
     
         return max_distance
     
    +
     def rotate_about_origin_degrees(origin, point, angle):
         """
         Rotate a point counterclockwise by a given angle around a given origin.
    @@ -388,6 +466,7 @@ def rotate_about_origin_degrees(origin, point, angle):
         """
         return rotate_about_origin(origin, point, math.radians(angle))
     
    +
     def rotate_about_origin(origin, point, angle):
         """
         Rotate a point counterclockwise by a given angle around a given origin.
    @@ -404,9 +483,8 @@ def rotate_about_origin(origin, point, angle):
         ret_y = orig_y + math.sin(angle) * (pnt_x - orig_x) + math.cos(angle) * (pnt_y - orig_y)
         return int(ret_x), int(ret_y)
     
    -def get_object_position(world_tform_cam,
    -                        visual_image, depth_image,
    -                        bounding_box, rotation_angle):
    +
    +def get_object_position(world_tform_cam, visual_image, depth_image, bounding_box, rotation_angle):
         """
         Extract the bounding box, then find the mode in that region.
     
    @@ -424,10 +502,8 @@ def get_object_position(world_tform_cam,
             return
     
         # Rotate bounding box back to original frame
    -    points = [(bounding_box[1], bounding_box[0]),
    -              (bounding_box[3], bounding_box[0]),
    -              (bounding_box[3], bounding_box[2]),
    -              (bounding_box[1], bounding_box[2])]
    +    points = [(bounding_box[1], bounding_box[0]), (bounding_box[3], bounding_box[0]),
    +              (bounding_box[3], bounding_box[2]), (bounding_box[1], bounding_box[2])]
     
         origin = (visual_image.shot.image.cols / 2, visual_image.shot.image.rows / 2)
     
    @@ -486,10 +562,11 @@ def get_object_position(world_tform_cam,
             obj_tform_camera = SE3Pose(tform_x, tform_y, tform_z, Quat())
     
             return world_tform_cam * obj_tform_camera
    -    except Exception as exc: # pylint: disable=broad-except
    +    except Exception as exc:  # pylint: disable=broad-except
             print(f'Error getting object position: {exc}')
             return
     
    +
     def _check_model_path(model_path):
         if model_path is None or \
         not os.path.exists(model_path) or \
    @@ -498,12 +575,14 @@ def _check_model_path(model_path):
             return False
         return True
     
    +
     def _check_and_load_json_classes(config_path):
         if os.path.isfile(config_path):
             with open(config_path) as json_classes:
    -            global COCO_CLASS_DICT # pylint: disable=global-statement
    +            global COCO_CLASS_DICT  # pylint: disable=global-statement
                 COCO_CLASS_DICT = json.load(json_classes)
     
    +
     def _find_highest_conf_source(processed_boxes_entry):
         highest_conf_source = None
         max_score = 0
    @@ -514,6 +593,7 @@ def _find_highest_conf_source(processed_boxes_entry):
                     max_score = capture['scores'][0]
         return highest_conf_source
     
    +
     def main(argv):
         """Command line interface.
     
    @@ -522,10 +602,12 @@ def main(argv):
         """
     
         parser = argparse.ArgumentParser()
    -    parser.add_argument("--model-path", required=True,
    -                        help="Local file path to the Tensorflow model, example pre-trained models \
    +    parser.add_argument(
    +        "--model-path", required=True,
    +        help="Local file path to the Tensorflow model, example pre-trained models \
                                 can be found at \
    -                            https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md")
    +                            https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md"
    +    )
         parser.add_argument("--classes", default='/classes.json', type=str,
                             help="File containing json mapping of object class IDs to class names")
         parser.add_argument("--number-tensorflow-processes", default=1, type=int,
    @@ -539,9 +621,10 @@ def main(argv):
         parser.add_argument(
             "--detection-class", default=1, type=int, help="Detection classes to use in the" +
             "Tensorflow model; Default is to use 1, which is a person in the Coco dataset")
    -    parser.add_argument("--max-processing-delay", default=7.0, type=float,
    -                        help="Maximum allowed delay for processing an image; " +
    -                        "any image older than this value will be skipped")
    +    parser.add_argument(
    +        "--max-processing-delay", default=7.0, type=float,
    +        help="Maximum allowed delay for processing an image; " +
    +        "any image older than this value will be skipped")
     
         bosdyn.client.util.add_common_arguments(parser)
         options = parser.parse_args(argv)
    @@ -553,7 +636,7 @@ def main(argv):
             # Check for classes json file, otherwise use the COCO class dictionary
             _check_and_load_json_classes(options.classes)
     
    -        global TENSORFLOW_PROCESS_BARRIER # pylint: disable=global-statement
    +        global TENSORFLOW_PROCESS_BARRIER  # pylint: disable=global-statement
             TENSORFLOW_PROCESS_BARRIER = Barrier(options.number_tensorflow_processes + 1)
             # Start Tensorflow processes
             start_tensorflow_processes(options.number_tensorflow_processes, options.model_path,
    @@ -616,8 +699,10 @@ def main(argv):
                 time.sleep(0.1)
     
             # Start image capture process
    -        image_capture_thread = Thread(target=capture_images,
    -                                      args=(image_task, options.sleep_between_capture,))
    +        image_capture_thread = Thread(target=capture_images, args=(
    +            image_task,
    +            options.sleep_between_capture,
    +        ))
             image_capture_thread.start()
             while True:
                 # This comes from the tensorflow processes and limits the rate of this loop
    @@ -634,11 +719,10 @@ def main(argv):
                     continue  # Skip image due to delay
     
                 # Find the transform to highest confidence object using the depth sensor
    -            world_tform_object = get_object_position(capture_to_use['world_tform_cam'],
    -                                                     capture_to_use['visual_image'],
    -                                                     capture_to_use['depth_image'],
    -                                                     capture_to_use['boxes'][0],
    -                                                     ROTATION_ANGLES[capture_to_use['source']])
    +            world_tform_object = get_object_position(
    +                capture_to_use['world_tform_cam'], capture_to_use['visual_image'],
    +                capture_to_use['depth_image'], capture_to_use['boxes'][0],
    +                ROTATION_ANGLES[capture_to_use['source']])
     
                 # get_object_position can fail if there is insufficient depth sensor information
                 if not world_tform_object:
    @@ -647,9 +731,7 @@ def main(argv):
                 scores = capture_to_use['scores']
                 print(f'Transform for object with confidence {scores[0]}: {world_tform_object}')
                 print(f'Process latency: {time.time() - capture_to_use["system_cap_time"]}')
    -            tag_cmd = get_go_to(world_tform_object,
    -                                robot_state_task.proto,
    -                                params_set)
    +            tag_cmd = get_go_to(world_tform_object, robot_state_task.proto, params_set)
                 end_time = 15.0
                 if tag_cmd is not None:
                     robot_command_client.robot_command(lease=None, command=tag_cmd,
    diff --git a/python/examples/spot_light/StateMachine.py b/python/examples/spot_light/StateMachine.py
    index e97312d68..0ce0006ae 100644
    --- a/python/examples/spot_light/StateMachine.py
    +++ b/python/examples/spot_light/StateMachine.py
    @@ -222,7 +222,9 @@ def _state_stand(self):
             self._robot.stand_up()
             self._state_idx = self._state_idx + 1
     
    -        print("\n Shine a light in Spot's front left camera, and Spot will tilt to follow the light.\n")
    +        print(
    +            "\n Shine a light in Spot's front left camera, and Spot will tilt to follow the light.\n"
    +        )
     
     
     #===================================================================================================
    diff --git a/python/examples/spot_light/requirements.txt b/python/examples/spot_light/requirements.txt
    index 2fd3aa43e..d9f5a9aeb 100644
    --- a/python/examples/spot_light/requirements.txt
    +++ b/python/examples/spot_light/requirements.txt
    @@ -1,6 +1,6 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.2
    +bosdyn-client >= 2.2
     
     numpy >= 1.16.4
     opencv-python >= 3.4.2.17
    diff --git a/python/examples/spot_tensorflow_detector/README.md b/python/examples/spot_tensorflow_detector/README.md
    index 0f3327d61..ec11e3953 100644
    --- a/python/examples/spot_tensorflow_detector/README.md
    +++ b/python/examples/spot_tensorflow_detector/README.md
    @@ -74,4 +74,4 @@ The value of all those parameters is updated asynchronously. The parameters are:
     
     The value for RAW_IMAGES_QUEUE increases rapidly in the beginning of the program, but it should decrease back to 0 in a few minutes. Based on the hardware where the program is running, increasing the value passed to the argument `--number-tensorflow-processes` reduces the size of this queue more quickly. Increasing the value of the argument `--sleep-between-capture` also reduces the size of this queue, but this option also reduces the real-time visualization of the environment around the Spot robot.
     
    -As an example, the `faster_rcnn_inception_v2_coco` Tensorflow model pre-trained on COCO dataset can be obtained [here](http://download.tensorflow.org/models/object_detection/faster_rcnn_inception_v2_coco_2018_01_28.tar.gz). Run the example with `--model-path` pointing to the `pb` file in that model and with `--detection-classes` argument set to `1` to detect people in the camera images. That model is not supported on Windows or MacOS.
    \ No newline at end of file
    +As an example, the `faster_rcnn_inception_v2_coco` Tensorflow model pre-trained on COCO dataset can be obtained [here](http://download.tensorflow.org/models/object_detection/faster_rcnn_inception_v2_coco_2018_01_28.tar.gz). Run the example with `--model-path` pointing to the `frozen_inference_graph.pb` file in that model and with `--detection-classes` argument set to `1` to detect people in the camera images. That model is not supported on Windows or MacOS.
    \ No newline at end of file
    diff --git a/python/examples/spot_tensorflow_detector/requirements.txt b/python/examples/spot_tensorflow_detector/requirements.txt
    index 357412eea..20040980c 100644
    --- a/python/examples/spot_tensorflow_detector/requirements.txt
    +++ b/python/examples/spot_tensorflow_detector/requirements.txt
    @@ -1,6 +1,6 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.0
    +bosdyn-client >= 2.0
     opencv-python >= 3.4.2.17
     Pillow==6.1.0
     scipy>=1.3.1
    diff --git a/python/examples/spot_tensorflow_detector/spot_tensorflow_detector.py b/python/examples/spot_tensorflow_detector/spot_tensorflow_detector.py
    index ebc741656..a3cb456a1 100644
    --- a/python/examples/spot_tensorflow_detector/spot_tensorflow_detector.py
    +++ b/python/examples/spot_tensorflow_detector/spot_tensorflow_detector.py
    @@ -80,13 +80,14 @@ def start_tensorflow_processes(num_processes, model_path, detection_classes, det
         """
     
         for counter in range(num_processes):
    -        process = Process(target=process_images, args=(
    -            counter,
    -            model_path,
    -            detection_classes,
    -            detection_threshold,
    -            max_processing_delay,
    -        ))
    +        process = Process(
    +            target=process_images, args=(
    +                counter,
    +                model_path,
    +                detection_classes,
    +                detection_threshold,
    +                max_processing_delay,
    +            ))
             process.start()
     
     
    @@ -258,12 +259,14 @@ def main(argv):
         parser.add_argument(
             "--detection-classes", help="Comma-separated list of detection classes " +
             "included in the Tensorflow model; Default is to use all classes in the model")
    -    parser.add_argument("--max-processing-delay", default=4.0, type=float,
    -                        help="Maximum allowed delay for processing an image; " +
    -                        "any image older than this value will be skipped")
    -    parser.add_argument("--max-display-delay", default=5.0, type=float,
    -                        help="Maximum allowed delay for displaying an image; " +
    -                        "any image older than this value will be skipped")
    +    parser.add_argument(
    +        "--max-processing-delay", default=4.0, type=float,
    +        help="Maximum allowed delay for processing an image; " +
    +        "any image older than this value will be skipped")
    +    parser.add_argument(
    +        "--max-display-delay", default=5.0, type=float,
    +        help="Maximum allowed delay for displaying an image; " +
    +        "any image older than this value will be skipped")
     
         bosdyn.client.util.add_common_arguments(parser)
         options = parser.parse_args(argv)
    @@ -281,8 +284,8 @@ def main(argv):
                 sys.exit(1)
     
             image_capture = SpotImageCapture()
    -        image_capture.initialize_sdk_no_motor_control(options.hostname,
    -                                                      options.username, options.password)
    +        image_capture.initialize_sdk_no_motor_control(options.hostname, options.username,
    +                                                      options.password)
     
             # Start Tensorflow processes
             start_tensorflow_processes(options.number_tensorflow_processes, options.model_path,
    diff --git a/python/examples/spot_tensorflow_detector/tensorflow_object_detection.py b/python/examples/spot_tensorflow_detector/tensorflow_object_detection.py
    index 90d90e71a..6a536a55a 100644
    --- a/python/examples/spot_tensorflow_detector/tensorflow_object_detection.py
    +++ b/python/examples/spot_tensorflow_detector/tensorflow_object_detection.py
    @@ -60,9 +60,7 @@ def process_frame(self, image):
             # Actual detection.
             (boxes, scores, classes, num) = self.sess.run([
                 self.detection_boxes, self.detection_scores, self.detection_classes, self.num_detections
    -        ], feed_dict={
    -            self.image_tensor: image_np_expanded
    -        })
    +        ], feed_dict={self.image_tensor: image_np_expanded})
     
             im_height, im_width, _ = image.shape
             boxes_list = [None for i in range(boxes.shape[1])]
    diff --git a/python/examples/stance/requirements.txt b/python/examples/stance/requirements.txt
    index ccf6f8ef8..e2e457ae8 100644
    --- a/python/examples/stance/requirements.txt
    +++ b/python/examples/stance/requirements.txt
    @@ -1,3 +1,3 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.1
    +bosdyn-client >= 2.1
    diff --git a/python/examples/stance/stance_in_place.py b/python/examples/stance/stance_in_place.py
    index aac5e6ae2..e9df93936 100644
    --- a/python/examples/stance/stance_in_place.py
    +++ b/python/examples/stance/stance_in_place.py
    @@ -46,7 +46,7 @@ def run(config):
         # Acquire lease
         lease_client = robot.ensure_client(bosdyn.client.lease.LeaseClient.default_service_name)
         lease = lease_client.acquire()
    -    
    +
         try:
             with bosdyn.client.lease.LeaseKeepAlive(lease_client):
                 command_client = robot.ensure_client(RobotCommandClient.default_service_name)
    diff --git a/python/examples/stitch_front_images/requirements.txt b/python/examples/stitch_front_images/requirements.txt
    index 42492fcbb..aa0e9a80c 100644
    --- a/python/examples/stitch_front_images/requirements.txt
    +++ b/python/examples/stitch_front_images/requirements.txt
    @@ -1,6 +1,6 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.0
    +bosdyn-client >= 2.0
     
     Pillow==6.1.0
     pygame==1.9.6
    diff --git a/python/examples/stitch_front_images/stitch_front_images.py b/python/examples/stitch_front_images/stitch_front_images.py
    index 66a7fc6b8..560ef7021 100644
    --- a/python/examples/stitch_front_images/stitch_front_images.py
    +++ b/python/examples/stitch_front_images/stitch_front_images.py
    @@ -24,8 +24,10 @@
     from ctypes import *
     from pygame.locals import *
     
    +
     class ImagePreppedForOpenGL():
         """Prep image for OpenGL from Spot image_response."""
    +
         def extract_image(self, image_response):
             """Return numpy_array of input image_response image."""
             image_format = image_response.shot.image.format
    @@ -72,8 +74,10 @@ def __init__(self, image_response):
     
             self.MVP = camera_projection_mat.dot(sensor_T_vo.to_matrix())
     
    +
     class ImageInsideOpenGL():
         """Create OpenGL Texture"""
    +
         def __init__(self, numpy_array):
             glEnable(GL_TEXTURE_2D)
             self.pointer = glGenTextures(1)
    @@ -86,8 +90,10 @@ def __init__(self, numpy_array):
         def update(self, numpy_array):
             """Update texture (no-op)"""
     
    +
     class CompiledShader():
         """OpenGL shader compile"""
    +
         def __init__(self, vert_shader, frag_shader):
             self.program = shaders.compileProgram( \
                 shaders.compileShader(vert_shader, GL_VERTEX_SHADER), \
    @@ -143,6 +149,7 @@ def set_image2_texture(self, image):
                 self.image2.update(image)
             self.set_texture(self.image2.pointer, self.image2_texture, 1)
     
    +
     def load_get_image_response_from_binary_file(file_path):
         """Read in image from image response"""
         if not os.path.exists(file_path):
    @@ -155,18 +162,22 @@ def load_get_image_response_from_binary_file(file_path):
     
         return _images
     
    +
     def proto_vec_T_numpy(vec):
         return numpy.array([vec.x, vec.y, vec.z])
     
    +
     def mat4mul3(mat, vec, vec4=1):
         ret = numpy.matmul(mat, numpy.append(vec, vec4))
         return ret[:-1]
     
    +
     def normalize(vec):
         norm = numpy.linalg.norm(vec)
         if norm == 0:
             raise ValueError("norm function returned 0.")
    -    return vec/norm
    +    return vec / norm
    +
     
     def draw_geometry(plane_wrt_vo, plane_norm_wrt_vo, sz_meters):
         """Draw as GL_TRIANGLES."""
    @@ -185,7 +196,7 @@ def draw_geometry(plane_wrt_vo, plane_norm_wrt_vo, sz_meters):
             plane_wrt_vo + plane_left_wrt_vo + plane_up_wrt_vo,
             plane_wrt_vo - plane_left_wrt_vo + plane_up_wrt_vo,
             plane_wrt_vo - plane_left_wrt_vo - plane_up_wrt_vo,
    -        )
    +    )
     
         indices = (0, 1, 2, 0, 2, 3)
     
    @@ -194,6 +205,7 @@ def draw_geometry(plane_wrt_vo, plane_norm_wrt_vo, sz_meters):
             glVertex3fv(vertices[index])
         glEnd()
     
    +
     def draw_routine(display, image_1, image_2, program):
         """OpenGL Draw"""
         rect_sz_meters = 7
    @@ -208,7 +220,7 @@ def draw_routine(display, image_1, image_2, program):
         eye_norm_wrt_body = numpy.array(image_1.body_T_image_sensor.rot.transform_point(0, 0, 1)) \
                           + numpy.array(image_2.body_T_image_sensor.rot.transform_point(0, 0, 1))
     
    -     # Make the virtual camera centered.
    +    # Make the virtual camera centered.
         eye_wrt_body[1] = 0
         eye_norm_wrt_body[1] = 0
     
    @@ -225,7 +237,7 @@ def draw_routine(display, image_1, image_2, program):
     
         glMatrixMode(GL_PROJECTION)
         glLoadIdentity()
    -    gluPerspective(110, (display[0]/display[1]), 0.1, 50.0)
    +    gluPerspective(110, (display[0] / display[1]), 0.1, 50.0)
     
         glMatrixMode(GL_MODELVIEW)
         glLoadIdentity()
    @@ -241,12 +253,13 @@ def draw_routine(display, image_1, image_2, program):
     
         draw_geometry(plane_wrt_vo, plane_norm_wrt_vo, rect_sz_meters)
     
    +
     def stitch(image_1, image_2, vert_shader, frag_shader):
         """Stitch two front fisheye images together"""
         pygame.init()
     
         display = (1080, 720)
    -    pygame.display.set_mode(display, pygame.DOUBLEBUF|pygame.OPENGL)
    +    pygame.display.set_mode(display, pygame.DOUBLEBUF | pygame.OPENGL)
         clock = pygame.time.Clock()
     
         program = CompiledShader(vert_shader, frag_shader)
    @@ -258,16 +271,17 @@ def stitch(image_1, image_2, vert_shader, frag_shader):
                     running = False
     
             glClearColor(0, 0, 255, 0)
    -        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
    +        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
     
             draw_routine(display, image_1, image_2, program)
     
             pygame.display.flip()
             clock.tick(60)
     
    +
     def main():
         """Top-level function to stitch together two Spot front camera images."""
    -    print("") # Separate output from imported package messages.
    +    print("")  # Separate output from imported package messages.
         import argparse
     
         parser = argparse.ArgumentParser(description=__doc__)
    @@ -296,5 +310,6 @@ def main():
     
         return True
     
    +
     if __name__ == '__main__':
         main()
    diff --git a/python/examples/tester_programs/requirements.txt b/python/examples/tester_programs/requirements.txt
    index 2ea267ac4..469da243b 100644
    --- a/python/examples/tester_programs/requirements.txt
    +++ b/python/examples/tester_programs/requirements.txt
    @@ -1,2 +1,2 @@
     -f ../../../prebuilt
    -bosdyn-client~=2.2
    +bosdyn-client>=2.2
    diff --git a/python/examples/tester_programs/testing_helpers.py b/python/examples/tester_programs/testing_helpers.py
    index a845fb8a7..3c83872d5 100644
    --- a/python/examples/tester_programs/testing_helpers.py
    +++ b/python/examples/tester_programs/testing_helpers.py
    @@ -8,7 +8,7 @@
     import sys
     import bosdyn.client
     import bosdyn.client.util
    -from bosdyn.client.util import strip_large_bytes_fields
    +from bosdyn.client.server_util import strip_large_bytes_fields
     from bosdyn.client.exceptions import (ResponseError, ServiceUnavailableError, TimedOutError,
                                           InvalidRequestError, UnableToConnectToRobotError)
     from bosdyn.client.directory import (DirectoryClient, NonexistentServiceError)
    diff --git a/python/examples/time_sync/requirements.txt b/python/examples/time_sync/requirements.txt
    index 847d1222a..d67237d10 100644
    --- a/python/examples/time_sync/requirements.txt
    +++ b/python/examples/time_sync/requirements.txt
    @@ -1,3 +1,3 @@
     -f ../../../prebuilt
    -bosdyn-client ~= 2.0
    +bosdyn-client >= 2.0
     
    diff --git a/python/examples/upload_choreographed_sequence/requirements.txt b/python/examples/upload_choreographed_sequence/requirements.txt
    index bf596ada8..03c1fd371 100644
    --- a/python/examples/upload_choreographed_sequence/requirements.txt
    +++ b/python/examples/upload_choreographed_sequence/requirements.txt
    @@ -1,4 +1,4 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.1
    -bosdyn-choreography-client ~= 2.1
    +bosdyn-client >= 2.1
    +bosdyn-choreography-client >= 2.1
    diff --git a/python/examples/upload_choreographed_sequence/upload_choreographed_sequence.py b/python/examples/upload_choreographed_sequence/upload_choreographed_sequence.py
    index ff16dda66..f93ba14ac 100644
    --- a/python/examples/upload_choreographed_sequence/upload_choreographed_sequence.py
    +++ b/python/examples/upload_choreographed_sequence/upload_choreographed_sequence.py
    @@ -18,6 +18,7 @@
     
     DEFAULT_DANCE = "default_dance.csq"
     
    +
     def main(argv):
         # Parse args
         parser = argparse.ArgumentParser()
    @@ -65,10 +66,12 @@ def main(argv):
         # Once the choreography is loaded into a protobuf message, upload the routine to the robot. We set
         # non_strict_parsing to true so that the robot will automatically correct any errors it find in the routine.
         try:
    -        upload_response = choreography_client.upload_choreography(choreography, non_strict_parsing=True)
    +        upload_response = choreography_client.upload_choreography(choreography,
    +                                                                  non_strict_parsing=True)
         except UnauthenticatedError as err:
    -        print("The robot license must contain 'choreography' permissions to upload and execute dances. "
    -              "Please contact Boston Dynamics Support to get the appropriate license file. ")
    +        print(
    +            "The robot license must contain 'choreography' permissions to upload and execute dances. "
    +            "Please contact Boston Dynamics Support to get the appropriate license file. ")
             return True
         except ResponseError as err:
             # Check if the ChoreographyService considers the uploaded routine as valid. If not, then the warnings must be
    @@ -108,6 +111,7 @@ def main(argv):
         robot.power_off()
         return True
     
    +
     if __name__ == "__main__":
         if not main(sys.argv[1:]):
             sys.exit(1)
    diff --git a/python/examples/velodyne_client/client.py b/python/examples/velodyne_client/client.py
    index 78329f65e..41a19e152 100644
    --- a/python/examples/velodyne_client/client.py
    +++ b/python/examples/velodyne_client/client.py
    @@ -34,17 +34,18 @@
     from bosdyn.client.math_helpers import Quat, SE3Pose
     from bosdyn.client.frame_helpers import get_odom_tform_body
     
    -
     matplotlib.use('Qt5agg')
     LOGGER = logging.getLogger(__name__)
     TEXT_SIZE = 10
     SPOT_YELLOW = '#FBD403'
     
    +
     def _update_thread(async_task):
         while True:
             async_task.update()
             time.sleep(0.01)
     
    +
     class AsyncPointCloud(AsyncPeriodicQuery):
         """Grab robot state."""
     
    @@ -55,6 +56,7 @@ def __init__(self, robot_state_client):
         def _start_query(self):
             return self._client.get_point_cloud_from_sources_async(["velodyne-point-cloud"])
     
    +
     class AsyncRobotState(AsyncPeriodicQuery):
         """Grab robot state."""
     
    @@ -65,11 +67,13 @@ def __init__(self, robot_state_client):
         def _start_query(self):
             return self._client.get_robot_state_async()
     
    +
     def window_closed(ax):
         fig = ax.figure.canvas.manager
         active_managers = plt._pylab_helpers.Gcf.figs.values()
         return not fig in active_managers
     
    +
     def set_axes_equal(ax):
         """Make axes of 3D plot have equal scale so that spheres appear as spheres,
         cubes as cubes, etc..  This is one possible solution to Matplotlib's
    @@ -92,12 +96,13 @@ def set_axes_equal(ax):
     
         # The plot bounding box is a sphere in the sense of the infinity
         # norm, hence I call half the max range the plot radius.
    -    plot_radius = 0.5*max([x_range, y_range, z_range])
    +    plot_radius = 0.5 * max([x_range, y_range, z_range])
     
         ax.set_xlim3d([x_middle - plot_radius, x_middle + plot_radius])
         ax.set_ylim3d([y_middle - plot_radius, y_middle + plot_radius])
         ax.set_zlim3d([z_middle - plot_radius, z_middle + plot_radius])
     
    +
     def main(argv):
         # The last argument should be the IP address of the robot. The app will use the directory to find
         # the velodyne and start getting data from it.
    @@ -110,7 +115,6 @@ def main(argv):
         robot.authenticate(options.username, options.password)
         robot.sync_with_directory()
     
    -
         _point_cloud_client = robot.ensure_client('velodyne-point-cloud')
         _robot_state_client = robot.ensure_client(RobotStateClient.default_service_name)
         _point_cloud_task = AsyncPointCloud(_point_cloud_client)
    @@ -146,16 +150,23 @@ def main(argv):
     
                 # Draw robot position and orientation on plot
                 if _robot_state_task.proto:
    -                odom_tform_body = get_odom_tform_body(_robot_state_task.proto.kinematic_state.transforms_snapshot).to_proto()
    +                odom_tform_body = get_odom_tform_body(
    +                    _robot_state_task.proto.kinematic_state.transforms_snapshot).to_proto()
                     helper_se3 = SE3Pose.from_obj(odom_tform_body)
                     odom_tform_butt = helper_se3.mult(body_tform_butt)
                     odom_tform_head = helper_se3.mult(body_tform_head)
    -                ax.plot([odom_tform_butt.x], [odom_tform_butt.y], [odom_tform_butt.z], 'o', color=SPOT_YELLOW, markersize=7, label='robot_butt')
    -                ax.plot([odom_tform_head.x], [odom_tform_head.y], [odom_tform_head.z], 'o', color=SPOT_YELLOW, markersize=7, label='robot_head')
    -                ax.text(odom_tform_butt.x,odom_tform_butt.y,odom_tform_butt.z, 'Spot Butt', size=TEXT_SIZE, zorder=1, color='k') 
    -                ax.text(odom_tform_head.x,odom_tform_head.y,odom_tform_head.z, 'Spot Head', size=TEXT_SIZE, zorder=1, color='k')
    -                ax.plot([odom_tform_butt.x, odom_tform_head.x], [odom_tform_butt.y,odom_tform_head.y],zs=[odom_tform_butt.z,odom_tform_head.z], linewidth=6, color=SPOT_YELLOW)
    -            
    +                ax.plot([odom_tform_butt.x], [odom_tform_butt.y], [odom_tform_butt.z], 'o',
    +                        color=SPOT_YELLOW, markersize=7, label='robot_butt')
    +                ax.plot([odom_tform_head.x], [odom_tform_head.y], [odom_tform_head.z], 'o',
    +                        color=SPOT_YELLOW, markersize=7, label='robot_head')
    +                ax.text(odom_tform_butt.x, odom_tform_butt.y, odom_tform_butt.z, 'Spot Butt',
    +                        size=TEXT_SIZE, zorder=1, color='k')
    +                ax.text(odom_tform_head.x, odom_tform_head.y, odom_tform_head.z, 'Spot Head',
    +                        size=TEXT_SIZE, zorder=1, color='k')
    +                ax.plot([odom_tform_butt.x, odom_tform_head.x],
    +                        [odom_tform_butt.y, odom_tform_head.y],
    +                        zs=[odom_tform_butt.z, odom_tform_head.z], linewidth=6, color=SPOT_YELLOW)
    +
                 # Plot point cloud data
                 ax.plot(plot_data[0::3], plot_data[1::3], plot_data[2::3], '.')
                 set_axes_equal(ax)
    @@ -163,7 +174,6 @@ def main(argv):
                 plt.pause(0.016)
                 if window_closed(ax):
                     sys.exit(0)
    -        
     
     
     if __name__ == '__main__':
    diff --git a/python/examples/velodyne_client/requirements.txt b/python/examples/velodyne_client/requirements.txt
    index d67e558a3..51eb09e70 100644
    --- a/python/examples/velodyne_client/requirements.txt
    +++ b/python/examples/velodyne_client/requirements.txt
    @@ -1,4 +1,4 @@
     -f ../../../prebuilt
    -bosdyn-client ~= 2.0
    +bosdyn-client >= 2.0
     matplotlib == 3.1.0
     numpy >= 1.16.4
    diff --git a/python/examples/visualizer/basic_streaming_visualizer.py b/python/examples/visualizer/basic_streaming_visualizer.py
    index ae4dd9daf..9fc6409a4 100644
    --- a/python/examples/visualizer/basic_streaming_visualizer.py
    +++ b/python/examples/visualizer/basic_streaming_visualizer.py
    @@ -274,8 +274,8 @@ def get_terrain_grid(local_grid_proto):
         """Generate a 3xN set of points representing the terrain local grid."""
         cells_pz_full = unpack_grid(local_grid_proto).astype(np.float32)
         # Populate the x,y values with a complete combination of all possible pairs for the dimensions in the grid extent.
    -    ys, xs = np.mgrid[0:local_grid_proto.local_grid.extent.num_cells_x, 0:local_grid_proto.
    -                      local_grid.extent.num_cells_y]
    +    ys, xs = np.mgrid[0:local_grid_proto.local_grid.extent.num_cells_x,
    +                      0:local_grid_proto.local_grid.extent.num_cells_y]
         # Numpy vstack makes it so that each column is (x,y,z) for a single terrain point. The height values (z) come from the
         # terrain grid's data field.
         pts = np.vstack(
    @@ -295,8 +295,8 @@ def create_vtk_no_step_grid(proto, robot_state_client):
         # Unpack the data field for the local grid.
         cells_no_step = unpack_grid(local_grid_proto).astype(np.float32)
         # Populate the x,y values with a complete combination of all possible pairs for the dimensions in the grid extent.
    -    ys, xs = np.mgrid[0:local_grid_proto.local_grid.extent.num_cells_x, 0:local_grid_proto.
    -                      local_grid.extent.num_cells_y]
    +    ys, xs = np.mgrid[0:local_grid_proto.local_grid.extent.num_cells_x,
    +                      0:local_grid_proto.local_grid.extent.num_cells_y]
         # Get the estimated height (z value) of the ground in the vision frame as if the robot was standing.
         transforms_snapshot = local_grid_proto.local_grid.transforms_snapshot
         vision_tform_body = get_a_tform_b(transforms_snapshot, VISION_FRAME_NAME, BODY_FRAME_NAME)
    @@ -335,8 +335,8 @@ def create_vtk_obstacle_grid(proto, robot_state_client):
         # Unpack the data field for the local grid.
         cells_obstacle_dist = unpack_grid(local_grid_proto).astype(np.float32)
         # Populate the x,y values with a complete combination of all possible pairs for the dimensions in the grid extent.
    -    ys, xs = np.mgrid[0:local_grid_proto.local_grid.extent.num_cells_x, 0:local_grid_proto.
    -                      local_grid.extent.num_cells_y]
    +    ys, xs = np.mgrid[0:local_grid_proto.local_grid.extent.num_cells_x,
    +                      0:local_grid_proto.local_grid.extent.num_cells_y]
     
         # Get the estimated height (z value) of the ground in the vision frame.
         transforms_snapshot = local_grid_proto.local_grid.transforms_snapshot
    @@ -475,7 +475,8 @@ def get_vtk_from_local_grid_proto(proto, robot_state_client):
     def compute_ground_height_in_vision_frame(robot_state_client):
         """Get the z-height of the ground plane in vision frame from the current robot state."""
         robot_state = robot_state_client.get_robot_state()
    -    vision_tform_ground_plane = get_a_tform_b(robot_state.kinematic_state.transforms_snapshot, VISION_FRAME_NAME, GROUND_PLANE_FRAME_NAME)
    +    vision_tform_ground_plane = get_a_tform_b(robot_state.kinematic_state.transforms_snapshot,
    +                                              VISION_FRAME_NAME, GROUND_PLANE_FRAME_NAME)
         return vision_tform_ground_plane.position.x
     
     
    @@ -515,7 +516,8 @@ def update_local_grid_actors(self, renwin, event):
             proto = self.local_grid_client.get_local_grids(
                 ['terrain', 'terrain_valid', 'intensity', 'no_step', 'obstacle_distance'])
             # Generate the polydata for each local grid source.
    -        obstacle_distance, no_step, terrain = get_vtk_from_local_grid_proto(proto, self.robot_state_client)
    +        obstacle_distance, no_step, terrain = get_vtk_from_local_grid_proto(
    +            proto, self.robot_state_client)
             # Update the polydata with the newest local grid data and re-render the windows.
             if 'no-step' in self.local_grid_types:
                 self.update_polydata_points(self.no_step_polydata, no_step)
    @@ -544,7 +546,8 @@ def init_local_grid_actors(self):
             proto = self.local_grid_client.get_local_grids(
                 ['terrain', 'terrain_valid', 'intensity', 'no_step', 'obstacle_distance'])
             # Generate the polydata for each local grid source.
    -        obstacle_distance, no_step, terrain = get_vtk_from_local_grid_proto(proto, self.robot_state_client)
    +        obstacle_distance, no_step, terrain = get_vtk_from_local_grid_proto(
    +            proto, self.robot_state_client)
             # Create a VTK actor for the local grid.
             for local_grid in proto:
                 if local_grid.local_grid_type_name == "terrain" and 'terrain' in self.local_grid_types:
    @@ -618,7 +621,8 @@ def main(argv):
         robot_state_actor = robot_state_timer.get_actor()
         renderer.AddActor(robot_state_actor)
     
    -    local_grid_timer = LocalGridTimedCallbackEvent(local_grid_client, robot_state_client, options.local_grid)
    +    local_grid_timer = LocalGridTimedCallbackEvent(local_grid_client, robot_state_client,
    +                                                   options.local_grid)
         grid_actors = local_grid_timer.get_actors()
         for actor in grid_actors:
             renderer.AddActor(actor)
    diff --git a/python/examples/visualizer/requirements.txt b/python/examples/visualizer/requirements.txt
    index 6ce71f3d5..7f74a1922 100644
    --- a/python/examples/visualizer/requirements.txt
    +++ b/python/examples/visualizer/requirements.txt
    @@ -1,5 +1,5 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.0
    +bosdyn-client >= 2.0
     numpy >= 1.16.4
     vtk==8.1.2
    diff --git a/python/examples/wasd/requirements.txt b/python/examples/wasd/requirements.txt
    index 885fb9926..f0b5e0e1e 100644
    --- a/python/examples/wasd/requirements.txt
    +++ b/python/examples/wasd/requirements.txt
    @@ -1,6 +1,6 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.1
    +bosdyn-client >= 2.1
     
     Pillow==6.1.0
     
    diff --git a/python/examples/wasd/wasd.py b/python/examples/wasd/wasd.py
    index 2ca61647b..1c583a612 100644
    --- a/python/examples/wasd/wasd.py
    +++ b/python/examples/wasd/wasd.py
    @@ -200,10 +200,10 @@ def __init__(self, robot):
             self._robot_command_client = robot.ensure_client(RobotCommandClient.default_service_name)
             self._robot_state_task = AsyncRobotState(self._robot_state_client)
             self._image_task = AsyncImageCapture(robot)
    -        self._async_tasks = AsyncTasks(
    -            [self._robot_state_task, self._image_task])
    +        self._async_tasks = AsyncTasks([self._robot_state_task, self._image_task])
             self._lock = threading.Lock()
             self._command_dictionary = {
    +            27: self._stop, # ESC key
                 ord('\t'): self._quit_program,
                 ord('T'): self._toggle_time_sync,
                 ord(' '): self._toggle_estop,
    @@ -330,7 +330,7 @@ def _drive_draw(self, stdscr, lease_keep_alive):
             stdscr.addstr(13, 0, "          [f]: Stand, [r]: Self-right               ")
             stdscr.addstr(14, 0, "          [v]: Sit, [b]: Battery-change             ")
             stdscr.addstr(15, 0, "          [wasd]: Directional strafing              ")
    -        stdscr.addstr(16, 0, "          [qe]: Turning                             ")
    +        stdscr.addstr(16, 0, "          [qe]: Turning, [ESC]: Stop              ")
             stdscr.addstr(17, 0, "          [l]: Return/Acquire lease                 ")
             stdscr.addstr(18, 0, "")
     
    @@ -363,12 +363,14 @@ def _try_grpc(self, desc, thunk):
                 return None
     
         def _try_grpc_async(self, desc, thunk):
    +
             def on_future_done(fut):
                 try:
                     fut.result()
                 except (ResponseError, RpcError, LeaseBaseError) as err:
                     self.add_message("Failed {}: {}".format(desc, err))
                     return None
    +
             future = thunk()
             future.add_done_callback(on_future_done)
     
    @@ -419,8 +421,8 @@ def _battery_change_pose(self):
             # Default HINT_RIGHT, maybe add option to choose direction?
             self._start_robot_command(
                 'battery_change_pose',
    -            RobotCommandBuilder.battery_change_pose_command(dir_hint=
    -            basic_command_pb2.BatteryChangePoseCommand.Request.HINT_RIGHT))
    +            RobotCommandBuilder.battery_change_pose_command(
    +                dir_hint=basic_command_pb2.BatteryChangePoseCommand.Request.HINT_RIGHT))
     
         def _sit(self):
             self._start_robot_command('sit', RobotCommandBuilder.synchro_sit_command())
    @@ -446,11 +448,13 @@ def _turn_left(self):
         def _turn_right(self):
             self._velocity_cmd_helper('turn_right', v_rot=-VELOCITY_BASE_ANGULAR)
     
    +    def _stop(self):
    +        self._start_robot_command('stop', RobotCommandBuilder.stop_command())
    +
         def _velocity_cmd_helper(self, desc='', v_x=0.0, v_y=0.0, v_rot=0.0):
    -        self._start_robot_command(desc,
    -                                  RobotCommandBuilder.synchro_velocity_command(
    -                                      v_x=v_x, v_y=v_y, v_rot=v_rot),
    -                                  end_time_secs=time.time() + VELOCITY_CMD_DURATION)
    +        self._start_robot_command(
    +            desc, RobotCommandBuilder.synchro_velocity_command(v_x=v_x, v_y=v_y, v_rot=v_rot),
    +            end_time_secs=time.time() + VELOCITY_CMD_DURATION)
     
         def _stow(self):
             self._start_robot_command('stow', RobotCommandBuilder.arm_stow_command())
    @@ -459,12 +463,12 @@ def _unstow(self):
             self._start_robot_command('stow', RobotCommandBuilder.arm_ready_command())
     
         def _return_to_origin(self):
    -        self._start_robot_command('fwd_and_rotate',
    -                                  RobotCommandBuilder.synchro_se2_trajectory_point_command(
    -                                      goal_x=0.0, goal_y=0.0, goal_heading=0.0,
    -                                      frame_name=ODOM_FRAME_NAME, params=None, body_height=0.0,
    -                                      locomotion_hint=spot_command_pb2.HINT_SPEED_SELECT_TROT),
    -                                  end_time_secs=time.time() + 20)
    +        self._start_robot_command(
    +            'fwd_and_rotate',
    +            RobotCommandBuilder.synchro_se2_trajectory_point_command(
    +                goal_x=0.0, goal_y=0.0, goal_heading=0.0, frame_name=ODOM_FRAME_NAME, params=None,
    +                body_height=0.0, locomotion_hint=spot_command_pb2.HINT_SPEED_SELECT_TROT),
    +            end_time_secs=time.time() + 20)
     
         def _take_ascii_image(self):
             source_name = "frontright_fisheye_image"
    @@ -638,14 +642,19 @@ def main():
             return False
     
         LOGGER.removeHandler(stream_handler)  # Don't use stream handler in curses mode.
    +
         try:
    -        # Run wasd interface in curses mode, then restore terminal config.
    -        curses.wrapper(wasd_interface.drive)
    +        try:
    +            # Prevent curses from introducing a 1 second delay for ESC key
    +            os.environ.setdefault('ESCDELAY', '0')
    +            # Run wasd interface in curses mode, then restore terminal config.
    +            curses.wrapper(wasd_interface.drive)
    +        finally:
    +            # Restore stream handler to show any exceptions or final messages.
    +            LOGGER.addHandler(stream_handler)
         except Exception as e:
    -        LOGGER.error("WASD has thrown an error: %s" % repr(e))
    +        LOGGER.error("WASD has thrown an error: [%r] %s", e, e)
         finally:
    -        # Restore stream handler after curses mode.
    -        LOGGER.addHandler(stream_handler)
             # Do any final cleanup steps.
             wasd_interface.shutdown()
     
    @@ -655,4 +664,4 @@ def main():
     if __name__ == "__main__":
         if not main():
             os._exit(1)
    -    os._exit(0)
    \ No newline at end of file
    +    os._exit(0)
    diff --git a/python/examples/web_cam_image_service/README.md b/python/examples/web_cam_image_service/README.md
    index f8bb8bd3f..80d0f3860 100644
    --- a/python/examples/web_cam_image_service/README.md
    +++ b/python/examples/web_cam_image_service/README.md
    @@ -53,6 +53,8 @@ There is an optional string argument `--codec` to specify a four character video
     ```
     This error was fixed for a linux experiment by providing the video codec argument as `--codec mjpg`.
     
    +There are optional arguments to change the camera's resolution if it is possible. The arguments `--res-width` and `--res-height` can adjust the image resolution for all captures completed by the service. If the input resolution is not achievable by the camera, the nearest/most similar resolution will be chosen and used. If no resolution is provided, the image service will use the camera's defaults.
    +
     Lastly, the command line argument `--show-debug-info` will allow a user to live-view the OpenCV output of the web cam video capture on their local computer. Only use this flag for debug purposes, as it will likely slow down the main example operation and reduce the performance of the image service.
     
     ## Debugging Tips
    diff --git a/python/examples/web_cam_image_service/docker-requirements.txt b/python/examples/web_cam_image_service/docker-requirements.txt
    index 491633672..a33c8fec7 100644
    --- a/python/examples/web_cam_image_service/docker-requirements.txt
    +++ b/python/examples/web_cam_image_service/docker-requirements.txt
    @@ -1,6 +1,6 @@
    -bosdyn-api==2.3.0
    -bosdyn-client==2.3.0
    -bosdyn-core==2.3.0
    +bosdyn-api==3.0.0
    +bosdyn-client==3.0.0
    +bosdyn-core==3.0.0
     certifi==2020.12.5
     chardet==4.0.0
     Deprecated==1.2.10
    diff --git a/python/examples/web_cam_image_service/requirements.txt b/python/examples/web_cam_image_service/requirements.txt
    index eeda5454d..83ed06c6f 100644
    --- a/python/examples/web_cam_image_service/requirements.txt
    +++ b/python/examples/web_cam_image_service/requirements.txt
    @@ -1,5 +1,5 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.2
    +bosdyn-client >= 2.2
     opencv-python>=4.2
     numpy >= 1.16.4
    diff --git a/python/examples/web_cam_image_service/web_cam_image_service.py b/python/examples/web_cam_image_service/web_cam_image_service.py
    index c0ca3874b..ba91ee59d 100644
    --- a/python/examples/web_cam_image_service/web_cam_image_service.py
    +++ b/python/examples/web_cam_image_service/web_cam_image_service.py
    @@ -15,7 +15,8 @@
     import bosdyn.util
     from bosdyn.client.directory_registration import (DirectoryRegistrationClient,
                                                       DirectoryRegistrationKeepAlive)
    -from bosdyn.client.util import GrpcServiceRunner, setup_logging
    +from bosdyn.client.util import setup_logging
    +from bosdyn.client.server_util import GrpcServiceRunner
     from bosdyn.api import image_pb2
     from bosdyn.api import image_service_pb2_grpc
     from bosdyn.client.image_service_helpers import VisualImageSource, CameraBaseImageServicer, CameraInterface
    @@ -26,11 +27,11 @@
     
     _LOGGER = logging.getLogger(__name__)
     
    -
     class WebCam(CameraInterface):
         """Provide access to the latest web cam data using openCV's VideoCapture."""
     
    -    def __init__(self, device_name, fps=30, show_debug_information=False, codec=""):
    +    def __init__(self, device_name, fps=30, show_debug_information=False, codec="",
    +                 res_width=-1, res_height=-1):
             self.show_debug_images = show_debug_information
     
             # Check if the user is passing an index to a camera port, i.e. "0" to get the first
    @@ -57,10 +58,14 @@ def __init__(self, device_name, fps=30, show_debug_information=False, codec=""):
             if not self.capture.isOpened():
                 # Unable to open a video capture connection to the specified device.
                 err = "Unable to open a cv2.VideoCapture connection to %s" % device_name
    -            _LOGGER.warn(err)
    +            _LOGGER.warning(err)
                 raise Exception(err)
     
             self.capture.set(cv2.CAP_PROP_FPS, fps)
    +        if res_width > 0 and res_height > 0:
    +            self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, res_width)
    +            self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, res_height)
    +            _LOGGER.info("Capture has resolution: %s x %s" % (self.capture.get(cv2.CAP_PROP_FRAME_WIDTH), self.capture.get(cv2.CAP_PROP_FRAME_HEIGHT)))
     
             # Use the codec input argument to determine the  OpenCV 'FourCC' variable is a byte code specifying
             # the video coedc (the compression/decompression software).
    @@ -71,19 +76,21 @@ def __init__(self, device_name, fps=30, show_debug_information=False, codec=""):
                 self.capture.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*codec.upper()))
             elif len(codec) > 0:
                 # Non-empty codec string, but it isn't the correct four character string we expect.
    -            raise Exception("The codec argument provided (%s) is the incorrect format. It should be a four character string." % codec)
    +            raise Exception(
    +                "The codec argument provided (%s) is the incorrect format. It should be a four character string."
    +                % codec)
     
             # Attempt to determine the gain and exposure for the camera.
             self.camera_exposure, self.camera_gain = None, None
             try:
                 self.camera_gain = self.capture.get(cv2.CAP_PROP_GAIN)
             except cv2.error as e:
    -            _LOGGER.warn("Unable to determine camera gain: %s", e)
    +            _LOGGER.warning("Unable to determine camera gain: %s", e)
                 self.camera_gain = None
             try:
                 self.camera_exposure = self.capture.get(cv2.CAP_PROP_EXPOSURE)
             except cv2.error as e:
    -            _LOGGER.warn("Unable to determine camera exposure time: %s", e)
    +            _LOGGER.warning("Unable to determine camera exposure time: %s", e)
                 self.camera_exposure = None
     
             # Determine the dimensions of the image.
    @@ -102,7 +109,7 @@ def blocking_capture(self):
                     cv2.imshow("WebCam Image Capture", image)
                     cv2.waitKey(1)
                 except Exception:
    -                _LOGGER.warn("Unable to display the webcam image captured.")
    +                _LOGGER.warning("Unable to display the webcam image captured.")
                     pass
             if success:
                 return image, capture_time
    @@ -151,17 +158,18 @@ def image_decode(self, image_data, image_proto, image_format, quality_percent=75
     
     def device_name_to_source_name(device_name):
         if type(device_name) == int:
    -        return "video"+str(device_name)
    +        return "video" + str(device_name)
         else:
             return os.path.basename(device_name)
     
     
     def make_webcam_image_service(bosdyn_sdk_robot, service_name, device_names,
    -                              show_debug_information=False, logger=None,
    -                              codec=""):
    +                              show_debug_information=False, logger=None, codec="",
    +                              res_width=-1, res_height=-1):
         image_sources = []
         for device in device_names:
    -        web_cam = WebCam(device, show_debug_information=show_debug_information, codec=codec)
    +        web_cam = WebCam(device, show_debug_information=show_debug_information, codec=codec,
    +                         res_width=res_width, res_height=res_height)
             img_src = VisualImageSource(web_cam.image_source_name, web_cam, rows=web_cam.rows,
                                         cols=web_cam.cols, gain=web_cam.camera_gain,
                                         exposure=web_cam.camera_exposure)
    @@ -169,15 +177,15 @@ def make_webcam_image_service(bosdyn_sdk_robot, service_name, device_names,
         return CameraBaseImageServicer(bosdyn_sdk_robot, service_name, image_sources, logger)
     
     
    -def run_service(bosdyn_sdk_robot, port, service_name, device_names,
    -                show_debug_information=False, logger=None,
    -                codec=""):
    +def run_service(bosdyn_sdk_robot, port, service_name, device_names, show_debug_information=False,
    +                logger=None, codec="", res_width=-1, res_height=-1):
         # Proto service specific function used to attach a servicer to a server.
         add_servicer_to_server_fn = image_service_pb2_grpc.add_ImageServiceServicer_to_server
     
         # Instance of the servicer to be run.
         service_servicer = make_webcam_image_service(bosdyn_sdk_robot, service_name, device_names,
    -                                                 show_debug_information, logger=logger, codec=codec)
    +                                                 show_debug_information, logger=logger, codec=codec,
    +                                                 res_width=res_width, res_height=res_height)
         return GrpcServiceRunner(service_servicer, add_servicer_to_server_fn, port, logger=logger)
     
     
    @@ -192,6 +200,8 @@ def add_web_cam_arguments(parser):
         parser.add_argument(
             '--codec', required=False, help="The four character video codec (compression format). For example, " +\
             "this is commonly 'DIVX' on windows or 'MJPG' on linux.", default="")
    +    parser.add_argument('--res-width', required=False, type=int, default=-1, help="Resolution width (pixels).")
    +    parser.add_argument('--res-height', required=False, type=int, default=-1, help="Resolution height (pixels).")
     
     if __name__ == '__main__':
         # Define all arguments used by this service.
    @@ -219,8 +229,8 @@ def add_web_cam_arguments(parser):
     
         # Create a service runner to start and maintain the service on background thread.
         service_runner = run_service(robot, options.port, DIRECTORY_NAME, devices,
    -                                 options.show_debug_info, logger=_LOGGER,
    -                                 codec=options.codec)
    +                                 options.show_debug_info, logger=_LOGGER, codec=options.codec,
    +                                 res_width=options.res_width, res_height=options.res_height)
     
         # Use a keep alive to register the service with the robot directory.
         dir_reg_client = robot.ensure_client(DirectoryRegistrationClient.default_service_name)
    diff --git a/python/examples/world_object_mutations/mutate_world_objects.py b/python/examples/world_object_mutations/mutate_world_objects.py
    index 21768cafa..68ee7df40 100644
    --- a/python/examples/world_object_mutations/mutate_world_objects.py
    +++ b/python/examples/world_object_mutations/mutate_world_objects.py
    @@ -87,7 +87,8 @@ def create_apriltag_object():
         # so the parent_frame must also be in the tree.
         vision_tform_special_frame = update_frame(tf=default_a_tform_b, position_change=(0, 0, -.2),
                                                   rotation_change=(0, 0, 0, 0))
    -    edges = add_edge_to_tree(edges, vision_tform_special_frame, VISION_FRAME_NAME, "my_special_frame")
    +    edges = add_edge_to_tree(edges, vision_tform_special_frame, VISION_FRAME_NAME,
    +                             "my_special_frame")
         snapshot = geom.FrameTreeSnapshot(child_to_parent_edge_map=edges)
     
         # Create the specific properties for the apriltag including the frame names for the transforms
    @@ -121,8 +122,7 @@ def main(argv):
     
         # List all world objects in the scene.
         world_objects = world_object_client.list_world_objects().world_objects
    -    print("Current World objects' ids " +
    -          str([obj.id for obj in world_objects]))
    +    print("Current World objects' ids " + str([obj.id for obj in world_objects]))
     
         # If there are any world objects in Spot's perception scene, then attempt to mutate one.
         # This should fail and return a STATUS_NO_PERMISSION since a client cannot mutate
    @@ -160,7 +160,8 @@ def main(argv):
                 full_snapshot = world_obj.transforms_snapshot
                 for edge in full_snapshot.child_to_parent_edge_map:
                     if edge == "my_special_frame":
    -                    print("The world object includes the custom frame vision_tform_my_special_frame!")
    +                    print(
    +                        "The world object includes the custom frame vision_tform_my_special_frame!")
     
         # Request to change an existing apriltag's dimensions. This will succeed because it is changing
         # an object that was added by a client program. We are using the ID returned by the service to
    @@ -169,8 +170,9 @@ def main(argv):
         tag_prop_modified = world_object_pb2.AprilTagProperties(tag_id=308,
                                                                 dimensions=geom.Vec2(x=.35, y=.35))
         wo_obj_to_change = world_object_pb2.WorldObject(
    -        id=added_apriltag_world_obj_id, name="world_obj_apriltag", transforms_snapshot=wo_obj_to_add.transforms_snapshot,
    -        acquisition_time=time_now, apriltag_properties=tag_prop_modified)
    +        id=added_apriltag_world_obj_id, name="world_obj_apriltag",
    +        transforms_snapshot=wo_obj_to_add.transforms_snapshot, acquisition_time=time_now,
    +        apriltag_properties=tag_prop_modified)
         print("World object X dimension of apriltag size before change: " +
               str([obj.apriltag_properties.dimensions.x for obj in world_objects]))
     
    @@ -218,10 +220,11 @@ def main(argv):
         world_objects = world_object_client.list_world_objects().world_objects
         for world_obj in world_objects:
             if world_obj.id == sphere_id:
    -            print("Found sphere named "+world_obj.name)
    +            print("Found sphere named " + world_obj.name)
                 full_snapshot = world_obj.transforms_snapshot
                 for edge in full_snapshot.child_to_parent_edge_map:
    -                print("Child frame name: " + edge + ". Parent frame name: " + full_snapshot.child_to_parent_edge_map[edge].parent_frame_name)
    +                print("Child frame name: " + edge + ". Parent frame name: " +
    +                      full_snapshot.child_to_parent_edge_map[edge].parent_frame_name)
         return True
     
     
    diff --git a/python/examples/world_object_mutations/requirements.txt b/python/examples/world_object_mutations/requirements.txt
    index 2f869f4df..058b6b085 100644
    --- a/python/examples/world_object_mutations/requirements.txt
    +++ b/python/examples/world_object_mutations/requirements.txt
    @@ -1,4 +1,4 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.0
    +bosdyn-client >= 2.0
     
    diff --git a/python/examples/world_object_with_image_coordinates/requirements.txt b/python/examples/world_object_with_image_coordinates/requirements.txt
    index b55ec2f63..27e4dca1f 100644
    --- a/python/examples/world_object_with_image_coordinates/requirements.txt
    +++ b/python/examples/world_object_with_image_coordinates/requirements.txt
    @@ -1,3 +1,3 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.0
    +bosdyn-client >= 2.0
    diff --git a/python/examples/xbox_controller/requirements.txt b/python/examples/xbox_controller/requirements.txt
    index 424d207b7..79cce5733 100644
    --- a/python/examples/xbox_controller/requirements.txt
    +++ b/python/examples/xbox_controller/requirements.txt
    @@ -1,6 +1,6 @@
     -f ../../../prebuilt
     
    -bosdyn-client ~= 2.1
    +bosdyn-client >= 2.1
     
     distro==1.4.0; platform_system != "Windows"
     XInput-Python==0.4.0; platform_system == "Windows"
    diff --git a/python/examples/xbox_controller/xbox_controller.py b/python/examples/xbox_controller/xbox_controller.py
    index 245f9b929..7ee3117d9 100644
    --- a/python/examples/xbox_controller/xbox_controller.py
    +++ b/python/examples/xbox_controller/xbox_controller.py
    @@ -17,9 +17,9 @@
     import bosdyn.client
     import bosdyn.client.util
     import bosdyn.client.estop
    -from bosdyn.client.lease import LeaseClient, _RESOURCE_BODY, LeaseKeepAlive
    +from bosdyn.client.lease import LeaseClient, LeaseKeepAlive
     from bosdyn.client.estop import EstopClient
    -from bosdyn.client.robot_command import RobotCommandBuilder, RobotCommandClient, blocking_stand
    +from bosdyn.client.robot_command import RobotCommandBuilder, RobotCommandClient
     from bosdyn.geometry import EulerZXY
     
     from xbox_joystick_factory import XboxJoystickFactory
    @@ -117,7 +117,8 @@ def initialize_robot_from_config(self, config):
                 locomotion_hint=spot_command_pb2.HINT_AUTO)
     
             # Print controls
    -        print(textwrap.dedent("""\
    +        print(
    +            textwrap.dedent("""\
     | Button Combination | Functionality            |
     |--------------------|--------------------------|
     | A                  | Walk                     |
    @@ -135,7 +136,7 @@ def initialize_robot_from_config(self, config):
     |                    |                          |
     | If Stand Mode      |                          |
     | - Left Stick       |                          |
    -| -- X               | Rotate body in roll axis |  
    +| -- X               | Rotate body in roll axis |
     | -- Y               | Control height           |
     | - Right Stick      |                          |
     | -- X               | Turn body in yaw axis    |
    @@ -149,7 +150,6 @@ def initialize_robot_from_config(self, config):
     | Back               | Exit                     |
             """))
     
    -
             # Describe the necessary steps before one can command the robot.
             print("Before you can command the robot: \n" + \
                 "\t1. Acquire a software E-Stop (Left Button + Right Button + B). \n" + \
    @@ -346,8 +346,8 @@ def _battery_change_pose(self):
             standing then roll to its [right]/left side for easier battery changing.
             """
     
    -        cmd = RobotCommandBuilder.battery_change_pose_command(dir_hint=
    -            basic_command_pb2.BatteryChangePoseCommand.Request.HINT_RIGHT)
    +        cmd = RobotCommandBuilder.battery_change_pose_command(
    +            dir_hint=basic_command_pb2.BatteryChangePoseCommand.Request.HINT_RIGHT)
             self._issue_robot_command(cmd)
     
         def _move(self, left_x, left_y, right_x):
    @@ -374,7 +374,7 @@ def _move(self, left_x, left_y, right_x):
                 stair_hint=self.mobility_params.stair_hint)
     
             cmd = RobotCommandBuilder.synchro_velocity_command(v_x=v_x, v_y=v_y, v_rot=v_rot,
    -                                                   params=self.mobility_params)
    +                                                           params=self.mobility_params)
             self._issue_robot_command(cmd, endtime=time.time() + VELOCITY_CMD_DURATION)
     
         def _orientation_cmd_helper(self, yaw=0.0, roll=0.0, pitch=0.0, height=0.0):
    @@ -392,7 +392,8 @@ def _orientation_cmd_helper(self, yaw=0.0, roll=0.0, pitch=0.0, height=0.0):
                 return
     
             orientation = EulerZXY(yaw, roll, pitch)
    -        cmd = RobotCommandBuilder.synchro_stand_command(body_height=height, footprint_R_body=orientation)
    +        cmd = RobotCommandBuilder.synchro_stand_command(body_height=height,
    +                                                        footprint_R_body=orientation)
             self._issue_robot_command(cmd, endtime=time.time() + VELOCITY_CMD_DURATION)
     
         def _change_height(self, direction):
    @@ -649,7 +650,8 @@ def main(argv):
             argv: List of command-line arguments.
         """
     
    -    parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, description=('''
    +    parser = argparse.ArgumentParser(
    +        formatter_class=argparse.RawTextHelpFormatter, description=('''
             Use this script to control the Spot robot from an Xbox controller. Press the Back
             controller button to safely power off the robot. Note that the example needs the E-Stop
             to be released. The estop_gui script from the estop SDK example can be used to release
    diff --git a/python/examples/xbox_controller/xbox_joystick_linux.py b/python/examples/xbox_controller/xbox_joystick_linux.py
    index 309a0f1bc..b8ef89d81 100644
    --- a/python/examples/xbox_controller/xbox_joystick_linux.py
    +++ b/python/examples/xbox_controller/xbox_joystick_linux.py
    @@ -48,7 +48,7 @@ def __init__(self, refresh_rate=30):
                 refresh_rate: Determines the maximum rate at which events are polled from xboxdrv.
             """
     
    -        super()
    +        super().__init__()
             try:
                 self.proc = subprocess.Popen(['xboxdrv', '--no-uinput', '--detach-kernel-driver'],
                                              stdout=subprocess.PIPE, bufsize=0)
    diff --git a/python/examples/xbox_controller/xbox_joystick_windows.py b/python/examples/xbox_controller/xbox_joystick_windows.py
    index bbbd0e9d3..80d6f5e82 100644
    --- a/python/examples/xbox_controller/xbox_joystick_windows.py
    +++ b/python/examples/xbox_controller/xbox_joystick_windows.py
    @@ -36,7 +36,7 @@ def __init__(self, refresh_rate=30):
                 refresh_rate: Determines the maximum rate at which events are polled from xboxdrv.
             """
     
    -        super()
    +        super().__init__()
             self.latest_state = None
             self.connected_controller_idx = None
             self.refresh_time = 0
    diff --git a/python/requirements-linux-pinned.txt b/python/requirements-linux-pinned.txt
    index f0c0c67b5..1efac8ab4 100644
    --- a/python/requirements-linux-pinned.txt
    +++ b/python/requirements-linux-pinned.txt
    @@ -1,5 +1,6 @@
     Deprecated==1.2.10
     grpcio==1.19.0
     protobuf==3.7.1
    +pynmea2==1.16.0
     six==1.12.0
     PyJWT
    \ No newline at end of file