Skip to content

Commit c0a7edc

Browse files
fix uint8 issue with vlm detections
1 parent be3c417 commit c0a7edc

4 files changed

Lines changed: 38 additions & 14 deletions

File tree

predicators/pretrained_model_interface.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,11 +306,12 @@ def _sample_completions(
306306
assert imgs is not None
307307
config = types.GenerateContentConfig(
308308
temperature=temperature,
309-
candidate_count=num_completions)
309+
candidate_count=num_completions,
310+
http_options=types.HttpOptions(timeout=15_000))
310311
response = self._client.models.generate_content(
311312
model=self._model_name,
312313
contents=[prompt] + imgs,
313-
config=config)
314+
config=config)
314315
return [response.text]
315316

316317
def get_id(self) -> str:

predicators/spot_utils/perception/object_detection.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -578,8 +578,9 @@ def _query_vlm(
578578
box = (x1, y1, x2, y2)
579579

580580
# Create a rectangular mask (no segmentation, just the bbox).
581-
mask = np.zeros((h, w), dtype=np.uint8)
582-
mask[int(y1):int(y2), int(x1):int(x2)] = 1
581+
# Must be bool so numpy uses boolean masking, not integer indexing.
582+
mask = np.zeros((h, w), dtype=bool)
583+
mask[int(y1):int(y2), int(x1):int(x2)] = True
583584

584585
score = float(det.get("confidence",
585586
CFG.spot_vlm_detection_default_score))

predicators/spot_utils/perception/spot_cameras.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import cv2
55
import numpy as np
66
from bosdyn.api import image_pb2
7-
from bosdyn.client.frame_helpers import BODY_FRAME_NAME, get_a_tform_b
7+
from bosdyn.client.frame_helpers import ODOM_FRAME_NAME, get_a_tform_b
88
from bosdyn.client.image import ImageClient, build_image_request
99
from bosdyn.client.sdk import Robot
1010
from numpy.typing import NDArray
@@ -60,12 +60,15 @@ def capture_images(
6060

6161
rgbds: Dict[str, RGBDImageWithContext] = {}
6262

63-
# Get the world->robot transform so we can store world->camera transforms
64-
# in the RGBDWithContexts.
63+
# Get world_tform_odom so we can compute world_tform_camera using
64+
# the odom frame from each image's own transforms_snapshot. This avoids
65+
# timing mismatches between localization and image capture — odom is
66+
# continuously tracked, so odom_tform_camera from the image snapshot is
67+
# exact at capture time, and world_tform_odom is stable between
68+
# localizations.
6569
if relocalize:
6670
localizer.localize()
67-
world_tform_body = localizer.get_last_robot_pose()
68-
body_tform_world = world_tform_body.inverse()
71+
world_tform_odom = localizer.get_world_tform_odom()
6972

7073
# Package all the requests together.
7174
img_reqs: image_pb2.ImageRequest = []
@@ -96,12 +99,12 @@ def capture_images(
9699
depth_img_resp = name_to_response[RGB_TO_DEPTH_CAMERAS[camera_name]]
97100
rgb_img = _image_response_to_image(rgb_img_resp)
98101
depth_img = _image_response_to_image(depth_img_resp)
99-
# Create transform.
100-
camera_tform_body = get_a_tform_b(
102+
# Create transform using odom frame from the image's own snapshot
103+
# to avoid timing mismatch between localization and image capture.
104+
odom_tform_camera = get_a_tform_b(
101105
rgb_img_resp.shot.transforms_snapshot,
102-
rgb_img_resp.shot.frame_name_image_sensor, BODY_FRAME_NAME)
103-
camera_tform_world = camera_tform_body * body_tform_world
104-
world_tform_camera = camera_tform_world.inverse()
106+
ODOM_FRAME_NAME, rgb_img_resp.shot.frame_name_image_sensor)
107+
world_tform_camera = world_tform_odom * odom_tform_camera
105108
# Extract other context.
106109
rot = ROTATION_ANGLE[camera_name]
107110
depth_scale = depth_img_resp.source.depth_scale

predicators/spot_utils/spot_localization.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def __init__(self, robot: Robot, upload_path: Path,
5353

5454
# Initialize robot pose, which will be updated in localize().
5555
self._robot_pose = math_helpers.SE3Pose(0, 0, 0, math_helpers.Quat())
56+
self._odom_tform_body_at_localization = None
5657
# Initialize the robot's position in the map.
5758
robot_state = get_robot_state(self._robot)
5859
current_odom_tform_body = get_odom_tform_body(
@@ -125,6 +126,19 @@ def get_last_robot_pose(self) -> math_helpers.SE3Pose:
125126
"""
126127
return self._robot_pose
127128

129+
def get_world_tform_odom(self) -> math_helpers.SE3Pose:
130+
"""Get world_tform_odom derived from the last localization.
131+
132+
This is: world_tform_body * (odom_tform_body)^{-1}
133+
Both captured at the same localization instant, so they are
134+
consistent. Use this to bridge from odom-frame transforms
135+
(available in image snapshots) to the world frame.
136+
"""
137+
assert self._odom_tform_body_at_localization is not None, \
138+
"Must call localize() before get_world_tform_odom()"
139+
body_tform_odom = self._odom_tform_body_at_localization.inverse()
140+
return self._robot_pose * body_tform_odom
141+
128142
def localize(self,
129143
num_retries: int = 10,
130144
retry_wait_time: float = 1.0) -> None:
@@ -150,6 +164,11 @@ def localize(self,
150164
retry_wait_time=retry_wait_time)
151165
logging.info("Localization succeeded.")
152166
self._robot_pose = math_helpers.SE3Pose.from_proto(transform)
167+
# Also store odom_tform_body at localization time so we can
168+
# compute world_tform_odom for bridging to image timestamps.
169+
robot_state = get_robot_state(self._robot)
170+
self._odom_tform_body_at_localization = get_odom_tform_body(
171+
robot_state.kinematic_state.transforms_snapshot)
153172
return None
154173

155174

0 commit comments

Comments
 (0)