Detect objects¶
Grab a camera frame, ask the platform’s cloud model where the named objects
are via client.platform.ai, then turn each box into a 3D point with the
aligned depth image when the robot has one; RGB-only robots get 2D boxes.
Edit TARGET to name what to find, for example “fire extinguishers” or
“open cabinet doors”. AI covers cost and how this differs from
on-robot models.
Boxes come from a general-purpose model, not a trained detector: they
are approximate, labels are free text, and score is always 1.0. The 3D
point is the median depth over the box, so it lands on the object’s visible
surface rather than its centre. Expect it to be pulled towards the background
for see-through objects (chair frames), objects partly out of frame, or
objects behind something nearer; narrow the region or use a mask for those.
from olo import Client
from olo.vision import camera_info_to_intrinsics, deproject_region
RGB_TOPIC = "wrist_rgbd/color_image"
# Leave empty for a colour-only camera; boxes then stay 2D.
DEPTH_TOPIC = "wrist_rgbd/depth_image"
CAMERA_INFO_TOPIC = "wrist_rgbd/camera_info"
# Name what to find, e.g. "fire extinguishers"; empty returns every notable object.
TARGET = ""
with Client() as client:
robot = client.robot()
image = robot.core.get_image(robot.core.topic(RGB_TOPIC, "sensor_msgs/msg/Image"))
depth = intrinsics = None
if DEPTH_TOPIC:
depth = robot.core.get_image(robot.core.topic(DEPTH_TOPIC, "sensor_msgs/msg/Image"))
camera_info = robot.core.get_latest(robot.core.topic(CAMERA_INFO_TOPIC, "sensor_msgs/msg/CameraInfo"))
intrinsics = camera_info_to_intrinsics(camera_info)
else:
print("No depth topic; reporting 2D boxes only")
# Boxes come back in this image's pixels.
result = client.platform.ai.detect_objects(image, prompt=TARGET)
print(f"Model: {result.model}")
if not result.image_usable:
print(f"Image unusable: {result.unusable_reason}")
print(f"Found {len(result.detections)} object(s)")
for detection in result.detections:
x_min, y_min, x_max, y_max = detection.box.to_xyxy()
line = f" {detection.label}: box ({x_min:.0f}, {y_min:.0f})-({x_max:.0f}, {y_max:.0f})"
if depth is not None and intrinsics is not None:
try:
# Median depth over the box, deprojected into the camera optical frame.
observation = deproject_region(depth, detection.box, intrinsics)
point = observation.point
line += f" at ({point.x:.2f}, {point.y:.2f}, {point.z:.2f}) m in {observation.frame_id}"
except ValueError:
line += " (no valid depth in box)"
print(line)
import { connect } from "olo/web";
import { cameraInfoToIntrinsics, deprojectRegion } from "olo/vision";
const RGB_TOPIC = "wrist_rgbd/color_image";
// Leave empty for a colour-only camera; boxes then stay 2D.
const DEPTH_TOPIC: string = "wrist_rgbd/depth_image";
const CAMERA_INFO_TOPIC = "wrist_rgbd/camera_info";
// Name what to find, e.g. "fire extinguishers"; empty returns every notable object.
const TARGET = "";
const client = connect();
const robot = await client.robot();
const image = await robot.core.getImage(robot.core.topic(RGB_TOPIC, "sensor_msgs/msg/Image"));
let depth;
let intrinsics;
if (DEPTH_TOPIC) {
depth = await robot.core.getImage(robot.core.topic(DEPTH_TOPIC, "sensor_msgs/msg/Image"));
const cameraInfo = await robot.core.getLatest(robot.core.topic(CAMERA_INFO_TOPIC, "sensor_msgs/msg/CameraInfo"));
intrinsics = cameraInfoToIntrinsics(cameraInfo);
} else {
console.log("No depth topic; reporting 2D boxes only");
}
// Boxes come back in this image's pixels.
const result = await client.platform.ai.detectObjects(image, { prompt: TARGET });
console.log(`Model: ${result.model}`);
if (!result.imageUsable) {
console.log(`Image unusable: ${result.unusableReason}`);
}
console.log(`Found ${result.detections.length} object(s)`);
for (const detection of result.detections) {
const [xMin, yMin, xMax, yMax] = detection.box.toXyxy();
let line = ` ${detection.label}: box (${xMin.toFixed(0)}, ${yMin.toFixed(0)})-(${xMax.toFixed(0)}, ${yMax.toFixed(0)})`;
if (depth && intrinsics) {
try {
// Median depth over the box, deprojected into the camera optical frame.
const observation = deprojectRegion(depth, detection.box, intrinsics);
const { x, y, z } = observation.point;
line += ` at (${x.toFixed(2)}, ${y.toFixed(2)}, ${z.toFixed(2)}) m in ${observation.frameId}`;
} catch {
line += " (no valid depth in box)";
}
}
console.log(line);
}